Minimum / maksimum iki sayı veren bir unix komutu var mı?


37

Okunan sayıları sınırlamak için bir komut arıyordum stdin.

Bu amaçla küçük bir senaryo yazdım (eleştiriye açıktır), ancak bunun için basit bir komut (standart ve basit kullanım (sanırım)) olup olmadığını merak ediyordum.

Asgari iki sayı bulan betiğim:

#!/bin/bash
# $1 limit

[ -z "$1" ] && { echo "Needs a limit as first argument." >&2; exit 1; }

read number

if [ "$number" -gt "$1" ]; then
        echo "$1"
else
        echo "$number"
fi

Yanıtlar:


20

Sadece iki sayıyı dcbenzerleriyle karşılaştırabilirsiniz:

dc -e "[$1]sM $2d $1<Mp"

... "$1"maksimum değeriniz nerededir ve "$2"daha küçükse yazdıracağınız sayıdır "$1". Bu ayrıca GNU’yu da gerektirir dc- ancak aynı şeyi taşınabilir şekilde yapabilirsiniz:

dc <<MAX
    [$1]sM $2d $1<Mp
MAX

Yukarıdaki durumların her ikisinde de, hassasiyeti 0 (varsayılan) gibi bir şeyden başka bir değere ayarlayabilirsiniz ${desired_precision}k. Her ikisi için de, her iki değerin de kesinlikle sayılar olduğunu doğrulamanız zorunludur, çünkü operatörle arama dcyapabilir .system()!

Aşağıdaki küçük betiği kullanarak (ve sonrakini) girişi grep -v \!|dcisteğe bağlı olarak sağlam bir şekilde işleyebilecek bir şey gibi doğrulamanız gerekir . Ayrıca, dcnegatif sayıları _önek yerine önekle yorumladığını da bilmelisiniz -- çünkü ikincisi çıkarma işlecidir.

Bunun dışında, bu komut dosyasıyla dc, \nsağlamak istediğinize göre sırayla dizilmiş ewline ayrılmış sayıları okuyacak ve wo'nun $maxhangisinin daha az olduğuna bağlı olarak her biri için değerinizi veya girişinizi yazdıracaksınız :

dc -e "${max}sm
       [ z 0=? d lm<M p s0 lTx ]ST
       [ ? z 0!=T q ]S?
       [ s0 lm ]SM lTx"

Yani ... Bunların her birini [kare parantez ]expanses bir olduğunu dc dize olan nesne Sherhangi biri - kendi ilgili diziye her aved T, ?ya M. dcBir dize ile yapılabilecek bazı diğer şeylerin yanı sıra x, bir makroyu aynı zamanda ekleyebilme. Doğru düzenlerseniz, tamamen işleyen küçük bir dcsenaryo yeterince basit bir şekilde derlenir.

dcbir yığın üzerinde çalışıyor . Tüm giriş nesneleri, her biri en üste istiflenir - her yeni giriş nesnesi, son üst nesneye ve altındaki tüm nesnelere yığılmış olarak yığılmış olarak aşağıya itilir. Bir nesneye en referanslar üst yığın değerine ve çoğu referanslar pop yığının bu üst (tek altındaki tüm nesneleri yukarı çeker) .

Ana yığının yanı sıra, ayrıca (en az) 256 dizi vardır ve her bir dizi öğesi, kendi başına bir yığınla birlikte gelir. Burada fazla kullanmıyorum. Ben böylece belirtildiği gibi sadece dizeleri depolamak listediği zaman onları OAD ve e xşartlı ecute onları ve ben syırttı $maxüst 'nin değerini mdizisi.

Her neyse, bu küçük parça, dckabuk betiğinin ne yaptığını büyük ölçüde yapıyor. GNU-ism -eseçeneğini kullanır - dcgenel olarak parametrelerini standarttan alır - ancak aynısını yapabilirsiniz:

echo "$script" | cat - /dev/tty | dc

... eğer $scriptyukarıdaki bit gibi görünüyorsa.

Gibi çalışır:

  • lTx- Bu lyuvarlanır ve xen üstte bulunan makroyu alır T (test için sanırım - genellikle bu adları rasgele seçerim) .
  • z 0=?- Test sonra yığın derinliğini w / test eder zve yığın boşsa (okuma: 0 nesne tutar)? makro çağırır .
  • ? z0!=T q- ?Makro, ? dcstdin'den bir girdi satırı okuyan yerleşik komut için adlandırılır , ancak aynı zamanda başka bir zyığın derinlik testi de ekledim , böylece qboş bir çizgi çekerse ya da EOF'ye çarparsa bütün küçük programa uyabilir. Ancak !yığını doldurmaz ve başarılı bir şekilde doldurursa, Ttekrar est çağırır .
  • d lm<M- Test daha sonra dyığının tepesini çoğaltacak ve $max (içinde saklandığı gibi m) ile karşılaştıracaktır . Daha mdüşük bir değer ise, makro dcçağırır M.
  • s0 lm- Msadece yığının üstünü çıkarır ve onu aptalca skalaya atar 0- yığını yığmanın sadece ucuz bir yolu. Ayrıca , est 'e dönmeden önce tekrar lses çıkarır .mT
  • p- Bu m, mevcut yığının tepesinden daha az ise, o zaman monu değiştirir ( dyinelenen, yine de) ve burada pkesilir, aksi halde girmez ve giriş ne olursa olsun, pbunun anlamı gelir.
  • s0- Sonra ( pyığını yığmadığı için) yığının tepesini 0tekrar dolduruyoruz ve sonra ...
  • lTx- yinelemeli lOAD Test kez daha sonra e xecute bunu tekrar.

Böylece bu küçük pasajı çalıştırabilir ve etkileşimli olarak terminalinize sayıları yazabilir ve dcgirdiğiniz sayıyı veya $maxyazdığınız sayı daha büyükse , değeri size geri yazdırabilirsiniz . Ayrıca herhangi bir dosyayı (bir boru gibi) standart girdi olarak kabul eder . Boş bir satıra veya EOF ile karşılaşana kadar okuma / karşılaştırma / yazdırma döngüsüne devam edecektir.

Bununla ilgili bazı notlar - Kabuk fonksiyonunuzdaki davranışı taklit etmek için yazdım, bu yüzden satır başına yalnızca bir sayı sağlam bir şekilde ele alınıyor. dcBununla birlikte, satır başına atmak istediğiniz gibi satır başına boşlukla ayrılmış sayıları kullanabilirsiniz. Bununla birlikte , yığından dolayı satırdaki son sayı, üzerinde çalıştığı ilk dcsayıdır ve satır başına birden fazla sayı yazdıysanız / yazdıysanız, yazıldığı gibi çıktısını tersine basar. bununla baş etmek için bir diziyi bir satırda saklamak, sonra çalışmaktır.

Bunun gibi, böyle:

dc -e "${max}sm
    [ d lm<M la 1+ d sa :a z0!=A ]SA
    [ la d ;ap s0 1- d sa 0!=P ]SP 
    [ ? z 0=q lAx lPx l?x ]S?
    [q]Sq [ s0 lm ]SM 0sa l?x"

Ama ... Bunu çok derinlemesine açıklamak isteyip istemediğimi bilmiyorum. dcYığındaki her bir değerde okuduğu gibi değerini veya $maxdeğerini indeksli bir dizide sakladığını ve yığının bir kez daha boş olduğunu algıladığında, başka bir okumaya çalışmadan önce her indekslenmiş nesneyi yazdırdığını söylemek yeterlidir. giriş hattı.

Ve böylece, ilk senaryo da ...

10 15 20 25 30    ##my input line
20
20
20
15
10                ##see what I mean?

İkinci yapar:

10 15 20 25 30    ##my input line
10                ##that's better
15
20
20                ##$max is 20 for both examples
20

Komutla ilk kez ayarlarsanız, keyfi kesinlikteki değişkenleri kullanabilirsiniz k. Ve igiriş veya oçıkış çaplarını bağımsız olarak değiştirebilirsiniz - bu bazen beklemeyebileceğiniz nedenlerden dolayı yararlı olabilir. Örneğin:

echo 100000o 10p|dc
 00010

... ilk önce dcçıkış yarıçapını 100000 değerine ayarlayan sonra 10 basar.


3
İki kez okuduktan sonra ne olduğu hakkında hiçbir fikriniz olmadığı için +1. Buna dalmak için zamanımı harcayacağım.
Minix

@Minix - meh - kafa karıştırıcı bulursanız en eski Unix programlama diline gitmeye gerek yok. Belki dcde parmak uçlarında tutmak için bir süre her seferinde birkaç numarayı sıkın.
mikeserv

1
@mikeserv Benim için çok geç. Umarım gelecek nesil beni uyarıcı bir masal olarak alır. Her yerde köşeli parantez ve harfler ...
Minix

@Minix - ne demek istiyorsun? Bunun için gittin mi? Çok iyi - dckararsız bir canavar, ancak herhangi bir Unix sistemindeki en hızlı ve en tuhaf yetenekli ortak yardımcı olabilir. Eşleştirildiğinde sed, bazı olağanüstü şeyler yapabilir. Onunla oynuyorum ve ddson zamanlarda olan canavarlığın yerini alabilmem için readline. İşte yaptığım bazı şeylerin küçük bir örneği . Bir Doing revin dcneredeyse çocuk oyuncağı.
mikeserv

1
@Minix - köşeli ayraçlar olmasına rağmen dikkatli olun. Bir dize içine köşeli ayraç koymak mümkün değildir - yapabileceğiniz en iyisidir [string]P91P93P[string]P. Bu yüzden, bu küçük parçanızı sedfaydalı bulabilirim: sed 's/[][]/]P93]&[1P[/g;s/[]3][]][[][1[]//g'hangi kareleri her zaman doğru bir dize yakın parantezle, sonra a P, sonra karenin ondalık ascii değeri ve diğeriyle değiştirmelidir P; sonra [dize devam etmek için açık bir köşeli ayraç. Dunno w / dc'nin string / sayısal dönüşüm yeteneklerini çözdüyseniz, ancak - özellikle w / birleştirildiğinde od- oldukça eğlenceli olabilir.
mikeserv

88

İki tamsayı ile uğraştığınızı biliyorsanız ave bo zaman ternary operatörünü kullanarak bu basit kabuk aritmetik açılımları sayısal maks vermesi için yeterlidir:

$(( a > b ? a : b ))

ve sayısal min:

$(( a < b ? a : b ))

Örneğin

$ a=10
$ b=20
$ max=$(( a > b ? a : b ))
$ min=$(( a < b ? a : b ))
$ echo $max
20
$ echo $min
10
$ a=30
$ max=$(( a > b ? a : b ))
$ min=$(( a < b ? a : b ))
$ echo $max
30
$ echo $min
20
$ 

İşte bunu gösteren bir kabuk betiği:

#!/usr/bin/env bash
[ -z "$1" ] && { echo "Needs a limit as first argument." >&2; exit 1; }
read number
echo Min: $(( $number  < $1 ? $number : $1 ))
echo Max: $(( $number  > $1 ? $number : $1 ))

Güzel cevap Lütfen küçük bir mod: bu "> =" için de kullanılabilir mi?
Sopalajo de Arrierez,

@SopalajodeArrierez Ne demek istediğinizi tam olarak anlamadım. Ayrıca yapabilirsiniz max=$(( a >= b ? a : b )), ancak sonuç tamamen aynıdır - eğer a ve b eşitse, hangisinin geri döndüğü önemli değildir. İstediğin bu mu?
Dijital Travma

Gerçekten, teşekkürler, DIJIOR Travma. Burada "> =" boole işlecinin mümkün olup olmadığını merak ediyordum.
Sopalajo de Arrierez

@SopalajodeArrierez if (( a >= b )); then echo a is greater than or equal to b; fi- istediğin bu mu? ( (( ))bunun yerine burada kullanılmasına dikkat edin $(( )))
Dijital Travma

Ah, evet, tamam. Şimdi anlıyorum. Kabuk genişlemesi hakkında pek bir şey bilmiyorum, bu yüzden genellikle koşullar arasında şaşkınlığa uğradım. Tekrar teşekkürler.
Sopalajo de Arrierez

25

sortve headbunu yapabilir:

numbers=(1 4 3 5 7 1 10 21 8)
printf "%d\n" "${numbers[@]}" | sort -rn | head -1       # => 21

2
Bunun, O(n log(n))max'ın verimli bir şekilde uygulanacağına dikkat edin O(n). Yine de bizim küçük n=2önemimiz, çünkü iki sürecin doğması genel olarak çok daha büyük.
Yalan Ryan

1
Doğru olsa da, @ glenn-jackman, soruyu sorduğuyla önemli olduğundan emin değilim. Bunu yapmanın en verimli yolu için bir talep yoktu. Bence soru kolaylık hakkındaydı.
David Hoelzer

1
@DavidHoelzer - Bu, burada verilen cevaplar arasında bile bunu yapmanın en etkili yolu değil. Sayı kümeleriyle çalışmak, burada bundan daha verimli (büyüklük sırasına göre) en az bir başka cevap ve eğer sadece iki tamsayı ile çalışmak durumunda bundan daha verimli başka bir cevap (büyüklük sırasına göre) varsa . Olsa da uygun (ama muhtemelen kabuk dizi, şahsen bırakırdı) .
mikeserv

1
Bu, aşağıdaki gibi diziler olmadan yapılabilir:numbers="1 4 3 5 7 1 10 21 8"; echo $numbers | tr ' ' "\n" | sort -rn | head -n 1
Ekran

1
Daha verimli bir yaklaşım muhtemelen şudur:max=0; for x in $numbers ; do test $x -gt $max && max=$x ; done
ngreen

6

Önceden tanımlanmış bir matematik fonksiyon kütüphanesini tanımlayabilir bcve ardından komut satırında kullanabilirsiniz.

Örneğin, aşağıdakini aşağıdaki gibi bir metin dosyasına ekleyin ~/MyExtensions.bc:

define max(a,b){
  if(a>b)
  { 
   return(a)
  }else{
   return(b)
  }
}

Şimdi arayabilirsin bc:

> echo 'max(60,54)' | bc ~/MyExtensions.bc
60

Bilginize, bu çevrimiçi mevcut gibi ücretsiz matematik kütüphane fonksiyonları vardır.

Bu dosyayı kullanarak, aşağıdakiler gibi daha karmaşık işlevleri kolayca hesaplayabilirsiniz GCD:

> echo 'gcd (60,54)' | bc ~/extensions.bc -l
6

Yanılmıyorsam, gerekirse bu tür işlevler çalıştırılabilir dosya ile de derlenebilir. Bence çoğu , GNU artık böyle olmasa da ( bugün GNU ve GNU kod kodlarının muazzam bir kısmını paylaşıyor olsalar da), bugünün bcsadece dcönyüzleri . Her neyse, buradaki en iyi cevap bu olabilir. bcdcbc
mikeserv

Bunu bir kabuk betiğinin dosyasında rahatça çağırmak için bc, işlev çağrısından hemen önce işlev tanımına yönlendirebilirsiniz . Sonra ikinci bir dosyaya gerek yok :)
tanius

5

Bir yorum için çok uzun:

Bunları örneğin sort | headveya sort | tailtaraklarla yapabilirken, hem kaynak hem de hata yönetimi konusunda oldukça düşük görünmektedir. Uygulama söz konusu olduğunda, birleşik giriş, sadece iki satırı kontrol etmek için 2 işlem üretme anlamına gelir. Bu biraz overkill gibi görünüyor.

Daha ciddi olan sorun, bilmeniz gereken çoğu durumda, girişin akıllıca olması, sadece sayıları içermesidir. @ glennjackmann'ın çözümü bunu zekice çözdü, çünkü printf %dtamsayılı olmayanları engellemeliydi . Ayrıca, kayan noktalarla çalışmaz (biçim belirticisini %fyuvarlama sorunlarıyla karşılaşacağınız yer olarak değiştirmezseniz).

test $1 -gt $2 Karşılaştırmada başarısız olup olmadığına dair bir gösterge verecektir (2'nin çıkış durumu, test sırasında bir hata olduğu anlamına gelir. Bu genellikle bir kabuk yerleşik olduğundan, ek bir işlem yoktur - yüzlerce sıradan bahsediyoruz) Daha hızlı yürütme katları, ancak tamsayılarla çalışır.

Birkaç kayan nokta sayısını karşılaştırmanız gerekirse, ilginç seçenek şunlar olabilir bc:

define x(a, b) {
    if (a > b) {
       return (a);
    }
    return (b);
 }

eşdeğeri olacak ve test $1 -gt $2kabuğun içinde kullanacaktı :

max () { printf '
    define x(a, b) {
        if (a > b) {
           return (a);
        }
        return (b);
     }
     x(%s, %s)
    ' $1 $2 | bc -l
}

printf | sort | head(iki sayı için) hala yaklaşık 2,5 kat daha hızlıdır .

GNU uzantılarına güvenebilirseniz , sayıları doğrudan komut dosyasına okumak için bcde read()işlevi kullanabilirsiniz bc.


Düşüncelerim tam olarak - Sadece ütüleniyordum, ama beni yendin: dc -e "${max}sm[z0=?dlm<Mps0lTx]ST[?z0!=Tq]S?[s0lm]SMlTx"- oh, dcher şeyi yapması dışında (eko hariç, ancak yapabiliyor olmasına rağmen) - stdin okur ve $maxhangisine bağlı olarak ya da giriş numarasını yazdırır. daha küçük. Neyse, açıklamak gerçekten umrumda değil ve cevabınız yazacağımdan daha iyi. Öyleyse benim oyum olsun.
mikeserv

@mikeserv aslında açıklanmış bir dcsenaryoya sahip olmak gerçekten iyi olurdu, RPN bu günlerde sık görülmüyor.
peterph

Ters Lehçe notasyonu (aka Postfix notasyonu). Ayrıca dcG / Ç'yi kendi başınıza yapabilirseniz, kendisinden daha zarif olur.
peterph

4

$ A ve $ b'den daha büyük bir değer elde etmek için şunu kullanın:

[ "$a" -gt "$b" ] && $a || $b

Ancak bunun etrafında bir şeye ihtiyacınız var, muhtemelen sayıyı kullanmak istemezsiniz, bu nedenle iki kullanımın "echo" değerini daha büyük göstermek için

[ "$a" -gt "$b" ] && echo $a || echo $b

Yukarıdakiler, örneğin bir kabuk fonksiyonuna güzel bir şekilde uyar;

max() {
   [ "$1" -gt "$2" ] && echo $1 || echo $2
}

İkisinden büyük olanını değişkene atamak için bu değiştirilmiş sürümü kullanın:

[ "$a" -gt "$b" ] && biggest=$a || biggest=$b

veya tanımlanmış işlevi kullanın:

biggest=$( max $a $b )

Fonksiyon değişimi ayrıca size düzgün bir şekilde kontrol giriş hatası ekleme imkanı sunar.

İki ondalık sayı / kayar nokta sayısının maksimum değerini döndürmek için awk

decimalmax() { 
   echo $1 $2 | awk '{if ($1 > $2) {print $1} else {print $2}}'; 
}

DÜZENLEME: Bu tekniği kullanarak, düzenleme / notunuza göre ters şekilde çalışan bir "limit" işlevi oluşturabilirsiniz. Bu işlev, ikisinin altını döndürür, örneğin:

limit() {
   [ "$1" -gt "$2" ] && echo $2 || echo $1
}

Yardımcı program işlevlerini ayrı bir dosyaya koymak, çağırmak myprogram.funcsve bir komut dosyasında aşağıdaki şekilde kullanmaktan hoşlanırım :

#!/bin/bash

# Initialization. Read in the utility functions
. ./myprogram.funcs

# Do stuff here
#
[ -z "$1" ] && { echo "Needs a limit as first argument." >&2; exit 1; }

read number
echo $( limit $1 $number )

FWIW Bu hala ne yaptığını yapar ve daha ayrıntılı olmasına rağmen sürümünüzü kadar verimli.

Daha kompakt olan form gerçekten daha iyi değildir, ancak komut dosyalarınızdaki karışıklığı önler. Çok basit if-then-else-fi yapıları varsa, komut dosyası hızla genişler.

Tek bir komut dosyasında daha büyük / daha küçük sayıları kontrol etmek için tekrar tekrar kullanmak isterseniz, bir işleve koyun. İşlev biçimi, hata ayıklamayı ve yeniden kullanımı kolaylaştırır ve örneğin tam sayı olmayan ondalık sayıları işleyebilmek için komut dosyasının bu bölümünü kolayca değiştirmenize olanak tanır.

Tek kullanımlık bir durum ise, sadece satır içi kodlayın.


4

Bir işlevi tanımlayabilirsiniz.

maxnum(){
    if [ $2 -gt $1 ]
    then
        echo $2
    else
        echo $1
    fi
}

Buna şöyle maxnum 54 42diyoruz ve yankılanıyor 54. İsterseniz, fonksiyonun içine doğrulama bilgisi ekleyebilirsiniz (örneğin iki argüman veya argüman gibi sayılar).


Çoğu kabuk kayan nokta aritmetik yapmaz. Ancak tamsayılar için işe yarıyor.
orion

1
Bu işlev gereksiz yere POSIX uyumlu değil. Değişim function maxnum {için maxnum() {ve ona çok daha fazla kabukları için çalışacağız.
Charles Duffy

2

Bir kabuk betiğinden, herhangi bir Java genel statik yöntemini (ve örneğin Math.min () ) kullanmanın bir yolu vardır . Linux'ta bash'tan:

. jsbInit
jsbStart 
A=2 
B=3 
C=$(jsb Math.min "$A" "$B")
echo "$C"

Bu, Java Kabuk Köprüsü'nü gerektirir https://sourceforge.net/projects/jsbridge/

Çok hızlı, çünkü yöntem çağrıları dahili olarak yönlendiriliyor ; işlem gerekli değil.


0

Çoğu insan sadece sort -n input | head -n1(veya kuyruğu) yapar, çoğu senaryo durumu için yeterince iyidir. Ancak, sütun yerine bir satırda sayılarınız varsa bu biraz sakardır - uygun bir biçimde ( tr ' ' '\n'veya benzer bir şekilde) yazdırmanız gerekir .

Kabuklar, sayısal işlemler için tam olarak ideal değildir, ancak kolayca içinde daha iyi olan başka bir programa kolayca aktarabilirsiniz. Tercihinize bağlı olarak, maksimum görüşme dc(biraz şaşkın, ancak ne yaptığınızı biliyorsanız, sorun değil - mikeserv'in cevabına bakın) veya awk 'NR==1{max=$1} {if($1>max){max=$1}} END { print max }'. Ya da muhtemelen perlveya pythontercih ederseniz. Bir çözüm (daha az bilinen yazılımı kurmaya ve kullanmaya istekli iseniz) olacaktır ised(özellikle verileriniz tek bir satırdaysa: yapmanız yeterlidir ised --l input.dat 'max$1').


Çünkü iki numara istiyorsun, hepsi bu. Bu yeterli olmalı:

python -c "print(max($j,$k))"

1
sys.argvpython2 -c 'import sys; print (max(sys.argv))' "$@"
Kullanırsanız

1
sort + headOverkill olan ancak pythonhesaplanmayan argümanlar hesaplanmaz.
mikeserv

Çizginin üstündeki tüm yöntemler büyük sayı kümelerini işlemek için tasarlanmıştır ve açıkça bu tür bir kullanım önerir (bir borudan veya dosyadan okuma). 2 argüman için min / max farklı hissettiren bir sorudur - akış yerine bir işlev gerektirir. Demek istediğim, akış yaklaşımının fazlaca kullanılması - kullandığınız araç isteğe bağlı, sadece kullandım pythonçünkü temiz.
orion

Ben çağırır bu önerilen çözüm kıvrımlara , ama ben bir olduğum için olabilir pythonbağnaz (bir çatal ve bir ek dev tercümana gerek olmadığı için ya) . Ya da belki ikisi de.
mikeserv

@mikeserv Onların tamsayı olduğunu bilsem ben de kullanırdım. Bahsettiğim tüm bu çözümler sayıların kayan olabileceği varsayımı altında - bash kayan nokta yapmıyor ve zsh sizin doğal kabuğunuz olmadığı sürece bir çatal (ve muhtemelen bir bıçak) ihtiyacınız olacak .
orion
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.