Yanıtlar:
Komut dosyası, $1
standart girişten ilk parametre olarak bir dosya adıyla çağrılırsa, aşağıdaki çözüm bir dosyadan okur .
while read line
do
echo "$line"
done < "${1:-/dev/stdin}"
İkamesi ${1:-...}
alır $1
aksi kendi sürecin standart girdi dosya adı kullanılır tanımlı.
/proc/$$/fd/0
ve /dev/stdin
? İkincisinin daha yaygın ve daha basit göründüğünü fark ettim.
-r
daha read
iyidir, böylece yanlışlıkla \
karakter yemeyin ; while IFS= read -r line
öncü ve sondaki boşlukları korumak için kullanın .
/bin/sh
- bash
veya dışında bir kabuk kullanıyor sh
musunuz?
Belki de en basit çözüm, stdin'i bir birleştirme yönlendirme operatörü ile yönlendirmektir:
#!/bin/bash
less <&0
Stdin, dosya tanımlayıcı sıfırdır. Yukarıdakiler, bash betiğinize aktarılan girişi daha az stdin'e gönderir.
Dosya tanımlayıcı yeniden yönlendirmesi hakkında daha fazla bilgi edinin .
<&0
Bu durumda kullanmanın bir yararı yoktur - örneğiniz, onunla veya onsuz aynı şekilde çalışacaktır - görünüşe göre, bir bash komut dosyasından çağırdığınız araçlar, varsayılan olarak komut dosyasının kendisiyle aynı stdin'i görür (komut dosyası önce tüketmedikçe).
İşte en basit yol:
#!/bin/sh
cat -
Kullanımı:
$ echo test | sh my_script.sh
test
Değişkene stdin atamak için şunu kullanabilirsiniz: STDIN=$(cat -)
ya da sadece STDIN=$(cat)
operatör gerekli olmadığı için ( @ mklement0 yorumuna göre ).
Her satırı standart girdiden ayrıştırmak için aşağıdaki komut dosyasını deneyin:
#!/bin/bash
while IFS= read -r line; do
printf '%s\n' "$line"
done
Dosyadan veya stdin'den okumak için (bağımsız değişken yoksa), aşağıdakine genişletebilirsiniz:
#!/bin/bash
file=${1--} # POSIX-compliant; ${1:--} can be used either.
while IFS= read -r line; do
printf '%s\n' "$line" # Or: env POSIXLY_CORRECT=1 echo "$line"
done < <(cat -- "$file")
Notlar:
-
read -r
- Ters eğik çizgi karakterine herhangi bir özel işlem yapmayın. Her ters eğik çizgiyi giriş satırının bir parçası olarak düşünün.- Ayar yapılmadan
IFS
, varsayılan olarak çizgilerin sırası Spaceve Tabbaşında ve sonunda diziler yoksayılır (kesilir).- Kullanım
printf
yerineecho
çizgi bir tek oluştuğunda boş satırları bastırılmasının önüne geçmek için-e
,-n
ya da-E
. Ancakenv POSIXLY_CORRECT=1 echo "$line"
, harici GNU'nuzuecho
destekleyen onu kullanan bir geçici çözüm vardır . Bakınız: "-e" yi nasıl yankılarım?
Bakınız: Hiçbir argüman aktarılmadığında stdin nasıl okunur? stackoverflow SE şirketinde
[ "$1" ] && FILE=$1 || FILE="-"
için FILE=${1:--}
. (Quibble: ortam değişkenleriyle ad çakışmalarını önlemek için tüm büyük harfli kabuk değişkenlerinden kaçınmak daha iyidir .)
${1:--}
olan tüm kabukları POSIX'deki benzeri içinde çalışması gerekir böylece, POSIX uyumlu. Tüm bu kabuklarda işe yaramayacak olan şey işlem ikamesi ( <(...)
); örneğin bash, ksh, zsh, ancak tire ile çalışmaz. Ayrıca, daha iyi eklemek -r
için için read
yanlışlıkla yemez böylece, komuta \
karakter; prepend IFS=
ön ve boşluk arka korumak için.
echo
bir çizgi oluşuyorsa: -e
, -n
ya -E
da gösterilmeyecektir. Bunu düzeltmek için, kullanmak gerekir printf
: printf '%s\n' "$line"
. Önceki düzenlememe dahil etmedim… Bu hatayı düzelttiğimde yaptığım düzenlemeler çok sık geri alındı :(
.
--
ilk argüman ise işe yaramaz'%s\n'
IFS=
ile read
ve printf
yerine echo
. :)
.
Bence bu basit bir yol:
$ cat reader.sh
#!/bin/bash
while read line; do
echo "reading: ${line}"
done < /dev/stdin
-
$ cat writer.sh
#!/bin/bash
for i in {0..5}; do
echo "line ${i}"
done
-
$ ./writer.sh | ./reader.sh
reading: line 0
reading: line 1
reading: line 2
reading: line 3
reading: line 4
reading: line 5
read
stdin'i okur varsayılan olarak öylesine var, gerek için < /dev/stdin
.
echo
Çözelti her yeni satır ekler IFS
giriş akışı keser. @ fgm'nin cevabı biraz değiştirilebilir:
cat "${1:-/dev/stdin}" > "${2:-/dev/stdout}"
read
davranış s': süre read
yok potansiyel karakter göre birden fazla jeton bölündü. içerdiği $IFS
bu sadece bir döner, tek Yalnızca belirtirseniz belirteci tek değişken adı (ama kapaklarının ve lider ve varsayılan olarak sonlarındaki boşluk).
read
ve $IFS
- echo
kendisi olmadan yeni satır ekler -n
bayrak. "Echo yardımcı programı, belirtilen işlenenleri tekli boş (` `) karakterlerle ayırarak ardından yeni satır (` \ n ') karakteri ile standart çıktıya yazar. "
\n
tarafından eklenen echo
: Perl en $_
içerir biten çizgi \n
bash en olurken, hattan okumak read
değildir. (@Gniourf_gniourf başka işaret ettiği gibi, ancak, daha sağlam bir yaklaşım kullanım için printf '%s\n'
yerine echo
).
Sorudaki Perl döngüsü , komut satırındaki tüm dosya adı bağımsız değişkenlerinden veya dosya belirtilmezse standart girdiden okunur . Gördüğüm tüm yanıtlar, herhangi bir dosya belirtilmemişse tek bir dosyayı veya standart girişi işliyor gibi görünüyor.
Genellikle UUOC (Yararsız Kullanım cat
) olarak doğru bir şekilde atılsa dacat
, iş için en iyi araç olduğu zamanlar vardır ve bunun bunlardan biri olduğu tartışmalıdır:
cat "$@" |
while read -r line
do
echo "$line"
done
Bunun tek dezavantajı, alt kabukta çalışan bir boru hattı oluşturmasıdır, bu nedenle while
döngüdeki değişken atamaları gibi şeylere boru hattı dışında erişilemez. Bunun bash
yolu İşlem Değiştirme'dir :
while read -r line
do
echo "$line"
done < <(cat "$@")
Bu, while
döngüyü ana kabukta çalışır halde bırakır , böylece döngüde ayarlanan değişkenlere döngü dışında erişilebilir.
>>EOF\n$(cat "$@")\nEOF
. Son olarak, bir kelime oyunu: Perl'de while IFS= read -r line
ne olduğuna dair daha iyi bir yaklaşımdır ( while (<>)
Perl de izini korurken de, önde gelen ve sondaki boşlukları korur \n
).
Perl'in davranışı, OP'de verilen kodla hiçbir veya birkaç argüman alabilir ve bir argüman tek bir tire ise -
bu stdin olarak anlaşılır. Dahası, dosya adına sahip olmak her zaman mümkündür $ARGV
. Şimdiye kadar verilen cevapların hiçbiri Perl'in bu açıdan davranışını gerçekten taklit etmiyor. İşte saf bir Bash olasılığı. Hile exec
uygun şekilde kullanmaktır .
#!/bin/bash
(($#)) || set -- -
while (($#)); do
{ [[ $1 = - ]] || exec < "$1"; } &&
while read -r; do
printf '%s\n' "$REPLY"
done
shift
done
Dosya Adi en uygun yer $1
.
Herhangi bir argüman verilmezse, yapay -
olarak ilk konumsal parametre olarak ayarladık . Daha sonra parametreleri döngüye sokuyoruz. Bir parametre değilse -
, standart girdiyi dosya adından ile yeniden yönlendiririz exec
. Bu yönlendirme başarılı olursa, bir while
döngü ile döngü yaparız . Standart REPLY
değişkeni kullanıyorum ve bu durumda sıfırlamanıza gerek yok IFS
. Başka bir ad istiyorsanız, bu şekilde sıfırlamanız gerekir IFS
(tabii ki bunu istemiyorsanız ve ne yaptığınızı bilmiyorsanız):
while IFS= read -r line; do
printf '%s\n' "$line"
done
Daha doğrusu ...
while IFS= read -r line ; do
printf "%s\n" "$line"
done < file
IFS=
ve -r
için read
her satır okunduğu bu komut aktarımı sağlar değiştirilmemiş (baştaki ve sondaki boşlukları dahil).
Lütfen aşağıdaki kodu deneyin:
while IFS= read -r line; do
echo "$line"
done < file
read
olmadan IFS=
ve -r
ve fakir $line
sağlıklı tırnak işaretleri olmadan.
read -r
Notasyonu beğenmedim . IMO, POSIX yanlış anladı; seçenek, ters eğik çizgilerin sonlandırılması için özel anlamı etkinleştirmeli, devre dışı bırakmamalı - böylece mevcut komut dosyaları (POSIX'in varlığından önceki) atlanmadığı -r
için kesilmeyecektir. Bununla birlikte, POSIX kabuğu ve yardımcı programlar standardının en eski versiyonu olan IEEE 1003.2 1992'nin bir parçası olduğunu gözlemliyorum, ancak o zaman bile bir ek olarak işaretlendi, bu yüzden uzun zamandır devam eden fırsatlar hakkında oyalanıyor. Kodum kullanmadığı için asla sorunla karşılaşmadım -r
; Şanslı olmalıyım. Beni bu konuda görmezden gel.
-r
standart olması gerektiğine gerçekten katılıyorum . Kullanmamanın belaya yol açtığı durumlarda olma ihtimalinin düşük olduğunu kabul ediyorum. Yine de, bozuk kod bozuk koddur. Düzenlemem ilk önce $line
tırnaklarını kötü şekilde kaçırmış olan fakir değişken tarafından tetiklendi . Ben o sıradayken düzelttim read
. Bunu düzeltmedim echo
çünkü geri döndürülen bir tür düzenleme. :(
.
Kod ${1:-/dev/stdin}
sadece ilk argümanı anlayacaktır, peki buna ne dersiniz.
ARGS='$*'
if [ -z "$*" ]; then
ARGS='-'
fi
eval "cat -- $ARGS" | while read line
do
echo "$line"
done
Bu cevapların hiçbirini kabul edilebilir bulmuyorum. Özellikle, kabul edilen cevap sadece ilk komut satırı parametresini işler ve geri kalanını yoksayar. Taklit etmeye çalıştığı Perl programı tüm komut satırı parametrelerini işler. Yani kabul edilen cevap soruya cevap bile vermiyor. Diğer cevaplar bash uzantılarını kullanır, gereksiz 'cat' komutları ekler, yalnızca girdiye çıktıya yankı verme basit durumu için çalışır veya sadece gereksiz derecede karmaşıktır.
Ancak, onlara bir miktar kredi vermeliyim çünkü bana bazı fikirler verdiler. İşte tam cevap:
#!/bin/sh
if [ $# = 0 ]
then
DEFAULT_INPUT_FILE=/dev/stdin
else
DEFAULT_INPUT_FILE=
fi
# Iterates over all parameters or /dev/stdin
for FILE in "$@" $DEFAULT_INPUT_FILE
do
while IFS= read -r LINE
do
# Do whatever you want with LINE here.
echo $LINE
done < "$FILE"
done
Yukarıdaki tüm cevapları birleştirdim ve ihtiyaçlarıma uygun bir kabuk işlevi yarattım. Bu, aralarında paylaşılan bir klasör bulduğum 2 Windows10 makinemin bir cygwin terminalinden. Aşağıdakileri idare edebilmem gerekiyor:
cat file.cpp | tx
tx < file.cpp
tx file.cpp
Belirli bir dosya adı belirtildiğinde, kopyalama sırasında aynı dosya adını kullanmam gerekir. Giriş veri akışı aracılığıyla piped nerede, o zaman saat dakika ve saniye sahip geçici bir dosya adı oluşturmak gerekir. Paylaşılan ana klasörde haftanın günlerinin alt klasörleri bulunur. Bu, organizasyon amaçları içindir.
İşte benim ihtiyaçlarım için nihai senaryo:
tx ()
{
if [ $# -eq 0 ]; then
local TMP=/tmp/tx.$(date +'%H%M%S')
while IFS= read -r line; do
echo "$line"
done < /dev/stdin > $TMP
cp $TMP //$OTHER/stargate/$(date +'%a')/
rm -f $TMP
else
[ -r $1 ] && cp $1 //$OTHER/stargate/$(date +'%a')/ || echo "cannot read file"
fi
}
Bunu daha da optimize etmek için görebileceğiniz herhangi bir yol varsa, bilmek istiyorum.
Aşağıdakiler standart ile çalışır sh
( dash
Debian ile test edilmiştir ) ve oldukça okunabilir, ancak bu bir zevk meselesi:
if [ -n "$1" ]; then
cat "$1"
else
cat
fi | commands_and_transformations
Ayrıntılar: İlk parametre boş değilse, cat
o dosya, diğer cat
standart girdi. Sonra tüm if
ifadenin çıktısı tarafından işlenir commands_and_transformations
.
cat "${1:--}" | any_command
. Kabuk değişkenlerini okumak ve yankılamak küçük dosyalar için işe yarayabilir, ancak çok iyi ölçeklenmez.
[ -n "$1" ]
basitleştirilebilir [ "$1" ]
.
Peki ya
for line in `cat`; do
something($line);
done
cat
komut satırına yerleştirilir. Komut satırı maksimum boyuttadır. Ayrıca bu satır satır okumaz, kelime kelime okumaz.