Bir dosyadan veya stdin'den girdi kabul eden bir script nasıl yazılır?


57

Bir dosya ismi argümanından veya stdin'den girdi kabul eden bir script nasıl yazılabilir?

örneğin, lessbu şekilde kullanabilirsiniz . tek yürütebilirsiniz less filenameve eşdeğer cat filename | less.

Bunu yapmak için kolay bir "kutunun dışında" bir yolu var mı? ya da tekerleği yeniden icat etmem ve senaryoda biraz mantık yazmam mı gerekiyor?


Sürece soru konuyla SU olduğu gibi @PlasmaPower, hiçbir orada gereklilik farklı SE sitesinde sormak. Birçok SE bölgesi örtüşüyor; Genel olarak, eğer soru konu dışı (bu durumda, oy kullanma hakkı yok) veya konu dışı olmadıkça, ancak çok fazla yanıt alamadıkça (bu durumda, başkanın moderatörlüğünü yapması gerekir) dikkat / göç, çapraz postaya değil).
Bob

Yanıtlar:


59

Eğer dosya argümanı betiğinizin ilk argümanı ise, bir argüman ( $1) olduğunu ve bunun bir dosya olduğunu test edin . Stdin'den başka okuma girişi -

Yani betiğin şöyle bir şey içerebilir:

#!/bin/bash
[ $# -ge 1 -a -f "$1" ] && input="$1" || input="-"
cat $input

örneğin o zaman komut dosyasını

./myscript.sh filename

veya

who | ./myscript.sh

Düzenleme Senaryo hakkında bazı açıklamalar:

[ $# -ge 1 -a -f "$1" ]- En az bir komut satırı argümanı ( $# -ge 1) AND (-a operatörü), birinci argüman bir dosyadır (-f "$ 1" bir dosya olup olmadığını sınamaktadır), o zaman test sonucu doğrudur.

&&mantıksal VE işleci kabuğudur. Eğer test doğruysa, o zaman atayın input="$1"ve cat $inputdosyayı çıkartacaktır.

||mantıksal VEYA işleci kabuğu. Test yanlışsa, aşağıdaki komutlar ||ayrıştırılır. giriş "-" olarak atanmıştır. Komut cat -klavyeden okur.

Özetle, eğer betik argümanı varsa ve bir dosya ise, değişken girişi dosya adına atanır. Geçerli bir argüman yoksa, kedi klavyeden okur.


ne yapar && input="$1" || input="-" ve neden testoperatörün dışında ?
cmo

Yardımcı olacağını umduğum bir açıklama içeren bir düzenleme ekledim.
suspectus,

Komut dosyasında birden fazla argüman varsa ( $@)?
g33kz0r

12

readstandart girdiden okur. Dosyadan ( ./script <someinput) veya pipe ( dosomething | ./script) üzerinden yönlendirmek, farklı çalışmasını sağlamaz.

Tek yapmanız gereken girdideki tüm satırlar arasında dolaşmaktır (ve dosyadaki satırlar üzerinde yinelenmekten farklı değildir).

(örnek kod, sadece bir satır işler)

#!/bin/bash

read var
echo $var

Standart girişinizin ilk satırını ekleyecektir ( <veya ile |).


Teşekkürler! diğer cevabı seçtim çünkü bu bana daha uygun oldu. başka bir senaryoyu sarıyordum ve tüm girdiler alınana kadar döngü yapmak istemedim (çok fazla girdi olabilirdi ... boşa gidecekti).
gilad hoch

4

Hangi kabuğu kullanmayı planladığından bahsetmiyorsun, bu yüzden kabuklarda oldukça standart şeyler olsa da bash farz edeceğim.

Dosya Argümanları

Argümanlara değişkenler aracılığıyla erişilebilir $1- $n( $0programı çalıştırmak için kullanılan komutu döndürür). Diyelim ki cataralarında sınırlayıcı olan az sayıda dosya içeren bir komut dosyası var :

#!/usr/bin/env bash
#
# Parameters:
#    1:   string delimiter between arguments 2-n
#    2-n: file(s) to cat out
for arg in ${@:2} # $@ is the array of arguments, ${@:2} slices it starting at 2.
do
   cat $arg
   echo $1
done

Bu durumda, bir dosya adını cat'a aktarıyoruz. Ancak, dosyadaki verileri dönüştürmek istiyorsanız (açıkça yazıp yeniden yazmadan), dosya içeriğini bir değişkende de saklayabilirsiniz:

file_contents=$(cat $filename)
[...do some stuff...]
echo $file_contents >> $new_filename

Stdin'den oku

Stdin'den okuduğumda, kabukların çoğu oldukça standart bir readyapıya sahip olsa da, istemlerin nasıl belirtildiği konusunda farklılıklar vardır (en azından).

Bash adam sayfası yerleşikleri bir oldukça özlü açıklaması var read, ama ben tercih Bash Hackerlar sayfasını.

basitçe:

read var_name

Birden Çok Değişken

Birden fazla değişken ayarlamak için, sadece şunlara birden fazla parametre adı sağlayın read:

read var1 var2 var3

read daha sonra stdin'den bir kelimeyi her değişkene yerleştirir, kalan tüm kelimeleri son değişkene atar.

λ read var1 var2 var3
thing1 thing2 thing3 thing4 thing5
λ echo $var1; echo $var2; echo $var3
thing1
thing2
thing3 thing4 thing5

Değişkenlerden daha az kelime girilirse, kalan değişkenler boştur (önceden ayarlanmış olsa bile):

λ read var1 var2 var3
thing1 thing2
λ echo $var1; echo $var2; echo $var3
thing1
thing2
# Empty line

istemleri

Bilgi -pistemi için sık sık bayrak kullanıyorum :

read -p "Enter filename: " filename

Not: ZSH ve KSH (ve belki diğerleri) bilgi istemleri için farklı bir sözdizimi kullanır:

read "filename?Enter filename: " # Everything following the '?' is the prompt

Varsayılan değerler

Bu gerçekten bir readoyun değil , ama ben çok fazla kullanıyorum read. Örneğin:

read -p "Y/[N]: " reply
reply=${reply:-N}

Temel olarak, değişken (cevap) varsa, kendini döndür, ancak boşsa, aşağıdaki parametreyi ("N") geri getir.


4

En basit yol stdin'i kendiniz yönlendirmektir:

if [ "$1" ] ; then exec < "$1" ; fi

Veya daha kısa ve öz formunu tercih ediyorsanız:

test "$1" && exec < "$1"

Şimdi betiğinizin geri kalanı sadece stdin'den okuyabilir. Elbette benzer şekilde dosya adının konumunu sabit kodlamak yerine daha fazla seçenek ayrıştırma ile yapabilirsiniz "$1".


execargümanı burada istediğimiz gibi olmayan bir komut olarak yürütmeye çalışacağım.
Suzana,

@Suzana_K: Burada olduğu gibi, hiçbir argüman olmadığı zaman. Bu durumda, alt süreçten ziyade sadece kabuğun kendisi için dosya tanımlayıcıların yerini alır.
R. ..

if [ "$1" ] ; then exec < "$1" ; fiBir test komut dosyasında kopyaladım ve komut unkown olduğu için hata mesajı veriyor. Veciz formu ile aynı.
Suzana,

1
@Suzana_K: Hangi kabuğu kullanıyorsunuz? Bu doğruysa, POSIX sh komutunun / Bourne kabuğunun çalışan bir uygulaması değildir.
R. ..

GNU bash 4.3.11, Linux Nint Qiana
Suzana,

3

bu şekilde davranan başka bir şey kullanmak (veya zincirlemek) ve kullanmak "$@"

Diyelim ki metindeki metin boşluklarını sekmelerle değiştirecek bir araç yazmak istiyorum.

trBunu yapmanın en bariz yolu, ancak sadece stdin'i kabul ediyor, bu yüzden zincirlememiz gerekiyor cat:

$ cat entab1.sh
#!/bin/sh

cat "$@"|tr -s ' ' '\t'
$ cat entab1.sh|./entab1.sh
#!/bin/sh

cat     "$@"|tr -s      '       '       '\t'
$ ./entab1.sh entab1.sh
#!/bin/sh

cat     "$@"|tr -s      '       '       '\t'
$ 

kullanılan aletin bu şekilde davrandığı bir örnek için, sedbunun yerine bunu tekrar uygulayabiliriz :

$ cat entab2.sh
#!/bin/sh

sed -r 's/ +/\t/g' "$@"
$ 

3

Ayrıca şunları da yapabilirsiniz:

#!/usr/bin/env bash

# Set variable input_file to either $1 or /dev/stdin, in case $1 is empty
# Note that this assumes that you are expecting the file name to operate on on $1
input_file="${1:-/dev/stdin}"

# You can now use "$input_file" as your file to operate on
cat "$input_file"

Bash daha düzgün parametre ikame püf noktaları için bkz bu .


1
Bu fantastik! Kullanıyorum uglifyjs < /dev/stdinve harika çalışıyor!
fregante

0

Ayrıca basit tutabilir ve bu kodu kullanabilirsiniz


Bir kod dosyası pass_it_on.sh bu kod ile oluşturduğunuzda,

#!/bin/bash

cat

Koşabilirsin

cat SOMEFILE.txt | ./pass_it_on.sh

stdin'in tüm içeriği ekrana yayılır.


Alternatif olarak, stdin'in bir kopyasını bir dosyada saklamak için bu kodu kullanın ve sonra ekrana doğru fırlatın.

#!/bin/bash

tmpFile=`mktemp`
cat > $tmpFile
cat $tmpFile    

ve burada başka bir örnek, belki daha okunaklı, burada açıklanmıştır:

http://mockingeye.com/blog/2013/01/22/reading-everything-stdin-in-a-bash-script/

#!/bin/bash

VALUE=$(cat)

echo "$VALUE"

İyi eğlenceler.

RaamEE


0

En kolay yol ve POSIX uyumlu:

file=${1--}

eşdeğerdir ${1:--}.

Sonra dosyayı her zamanki gibi okuyun:

while IFS= read -r line; do
  printf '%s\n' "$line" # Or: env POSIXLY_CORRECT=1 echo "$line"
done < <(cat -- "$file")
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.