Bir dosyanın satırlarını komutun bağımsız değişkenleri olarak nasıl kullanabilirim?


159

Diyelim ki argümanları foo.txtbelirten bir Ndosyam var

arg1
arg2
...
argN

ki komuta geçmem gerekiyor my_command

Bir dosyanın satırlarını komutun bağımsız değişkenleri olarak nasıl kullanabilirim?


11
"argümanlar", çoğul veya "argüman", tek mi? Kabul edilen cevap yalnızca, gövde metninin sorguladığı tek argüman durumunda doğrudur, ancak konunun sorguladığı çok argümanlı durumda doğru değildir.
Charles Duffy

Aşağıdaki 4 cevap genellikle aynı sonuçlara sahiptir, ancak biraz farklı anlambilime sahiptir. Şimdi neden bash betikleri yazmayı bıraktığımı hatırlıyorum: P
Navin

Yanıtlar:


237

Kabuğunuz bash ise (diğerlerinin yanı sıra), kısayolu $(cat afile)şudur $(< afile):

mycommand "$(< file.txt)"

'Komut Değiştirme' bölümündeki bash man sayfasında belgelenmiştir.

Alternatif olarak, komutunuzu stdin'den okuyun, bu yüzden: mycommand < file.txt


11
Bilgiçlikçi olmak, <operatörü kullanmak için bir kısayol değildir . Bu, kabuğun, catyönlendirme özellikleri için ikili dosyayı yürütmek yerine yeniden yönlendirmeyi gerçekleştireceği anlamına gelir .
lstyls

2
Bu bir çekirdek ayarıdır, bu yüzden çekirdeği yeniden derlemeniz gerekir. Veya xargs komutunu araştırın
glenn jackman

3
@ykaner çünkü olmadan "$(…)", içeriği argüman olarak değil file.txt, standart girdisine aktarılır mycommand. komutu çalıştırmak ve çıktıyı dize olarak geri vermek"$(…)" anlamına gelir ; burada “komut” sadece dosyayı okur, ancak daha karmaşık olabilir.
törzsmókus

3
Alıntıları kaybetmedikçe benim için çalışmıyor. Tırnak işaretleri ile tüm dosyayı 1 argüman olarak alır. Tırnak işaretleri olmadan, her satırı ayrı bir argüman olarak yorumlar.
Pete

1
@LarsH kesinlikle haklısın. Bu daha az kafa karıştırıcı bir yol, teşekkürler.
lstyls

37

Daha önce de belirtildiği gibi, ters tırnakları veya kullanabilirsiniz $(cat filename).

Bahsetmediğim ve not edilmesi gereken önemli olan, kabuğun, boşluğa göre o dosyanın içeriğini parçalayacağını ve komutunuza bir argüman olarak bulduğu her "sözcüğü" vereceğini hatırlamanız gerektiğidir. Boşluk, kaçış dizileri vb. İçerebilmesi için bir komut satırı bağımsız değişkenini tırnak içine alabilmenize rağmen, dosyadan okumak aynı şeyi yapmaz. Örneğin, dosyanız şunları içeriyorsa:

a "b c" d

alacağınız argümanlar:

a
"b
c"
d

Her satırı bağımsız değişken olarak çekmek istiyorsanız while / read / do yapısını kullanın:

while read i ; do command_name $i ; done < filename

Bahsetmeliydim, bash kullandığınızı varsayıyorum. Orada başka mermiler olduğunu fark ettim, ama üzerinde çalıştığım * nix makinelerinin neredeyse tamamı bash ya da eşdeğeri koştu. IIRC, bu sözdizimi ksh ve zsh için aynı şekilde çalışmalıdır.
Will

1
read -rTers eğik çizgi-kaçış dizilerini genişletmek istemediğiniz sürece okuma olmalıdır - ve NUL, özellikle geçmekte olduğunuz argümanlar gerçek adlar içeren dosya adları gibi şeyler ise, yeni satırdan daha güvenli bir sınırlayıcıdır. Ayrıca, IFS'yi temizlemeden, dolaylı olarak temizlenen önde gelen ve sondaki boşlukları elde edersiniz i.
Charles Duffy

18
command `< file`

dosya içeriğini stdin'deki komuta iletir, ancak yeni satırları çıkarır, yani her satır üzerinde ayrı ayrı yineleme yapamazsınız. Bunun için 'for' döngüsüne sahip bir komut dosyası yazabilirsiniz:

for line in `cat input_file`; do some_command "$line"; done

Veya (çok satırlı varyant):

for line in `cat input_file`
do
    some_command "$line"
done

Veya ( $()yerine çok satırlı varyant ``):

for line in $(cat input_file)
do
    some_command "$line"
done

Referanslar:

  1. Döngü sözdizimi için: https://www.cyberciti.biz/faq/bash-for-loop/

Ayrıca, geri < filetıklamaları, aslında hiç bir yönlendirme gerçekleştirmediği anlamına gelir command.
Charles Duffy

3
(Bunu iptal eden insanlar gerçekten test ettiler mi? Bash veya POSIX sh'de command `< file` çalışmaz ; zsh farklı bir konudur, ancak bu sorunun konusu olan kabuk bu değildir).
Charles Duffy

@CharlesDuffy Nasıl çalışmadığı konusunda daha spesifik olabilir misiniz?
mwfearnley

@CharlesDuffy Bu, bash 3.2 ve zsh 5.3'te çalışır. Bu işlem sh, ash ve dash olarak çalışmaz, ancak $ (<file) öğesinde de çalışmaz.
Arnie97

1
@mwfearnley, bu özgüllüğü sağlamaktan mutluluk duyuyoruz. Amaç çizgiler üzerinden tekrarlamak değil mi? Hello WorldBir satıra koy . Bunu ilk çalıştırmak göreceksiniz some_command Hello, sonra some_command World, değil some_command "Hello World" veya some_command Hello World.
Charles Duffy

15

Bunu ters çentikler kullanarak yaparsınız:

echo World > file.txt
echo Hello `cat file.txt`

4
Bu bir argüman oluşturmaz - alıntı yapılmadığından, dize bölünmesine tabidir, bu nedenle echo "Hello * Starry * World" > file.txtilk adımda yayınlarsanız , ikinci komuta en az dört ayrı argüman iletilirsiniz - ve muhtemelen gibi daha *s dosyaların adlarına artıracağı geçerli dizinde sunuyoruz.
Charles Duffy

3
... ve glob genişletmesi çalıştırdığı için, dosyada tam olarak ne olduğunu yaymıyor . Ve bir komut ikame işlemi gerçekleştirdiği için, son derece verimsizdir - aslında fork()stdoutuna bağlı bir FIFO ile bir /bin/catalt kabuktan çıkıyor, daha sonra bu alt kabuğun bir çocuğu olarak çağırıyor ve daha sonra çıktıyı FIFO üzerinden okuyor; $(<file.txt)dosyanın içeriğini alt kabuklar veya FIFO'lar olmadan doğrudan bash'a okuyan ile karşılaştırın .
Charles Duffy

11

Bunu mümkün olan her komut satırı argümanı (boşluklu değerler, satırsonu değerleri, değişmez tırnak karakterleri içeren değerler, yazdırılamayan değerler, glob karakterleri olan değerler, vb.) İçin çalışan sağlam bir şekilde yapmak istiyorsanız, biraz daha ilginç.


Bir dosyaya yazmak için, bir dizi argüman verildi:

printf '%s\0' "${arguments[@]}" >file

... ile değiştirin "argument one", "argument two"uygun olarak, vb.


Bu dosyadan okumak ve içeriğini kullanmak için (bash, ksh93 veya dizileri olan yeni bir kabukta):

declare -a args=()
while IFS='' read -r -d '' item; do
  args+=( "$item" )
done <file
run_your_command "${args[@]}"

Bu dosyadan okumak ve içeriğini kullanmak için (dizileri olmayan bir kabukta; bunun yerel komut satırı bağımsız değişken listenizin üzerine yazacağını ve bu nedenle en iyi şekilde işlevin bağımsız değişkenlerinin üzerine yazacağınızı, genel liste):

set --
while IFS='' read -r -d '' item; do
  set -- "$@" "$item"
done <file
run_your_command "$@"

Not -d(farklı sonu hattı ayraç kullanılmasına izin veren) bir POSIX olmayan uzantısıdır ve diziler olmayan bir kabuk da bunu desteklemez. Bu durumda, NUL ile sınırlandırılmış içeriği evalgüvenli bir forma dönüştürmek için kabuk olmayan bir dil kullanmanız gerekebilir :

quoted_list() {
  ## Works with either Python 2.x or 3.x
  python -c '
import sys, pipes, shlex
quote = pipes.quote if hasattr(pipes, "quote") else shlex.quote
print(" ".join([quote(s) for s in sys.stdin.read().split("\0")][:-1]))
  '
}

eval "set -- $(quoted_list <file)"
run_your_command "$@"

6

Bir dosyanın içeriğini bir komuta bağımsız değişken olarak nasıl aktarırım:

./foo --bar "$(cat ./bar.txt)"

1
Bu - ancak önemli ölçüde daha az verimli olduğu için - etkili bir şekilde eşdeğerdir $(<bar.txt)(bash gibi bir kabuğu uygun uzantı ile kullanırken). $(<foo)özel bir durumdur: normal komut ikamesinin aksine, kullanıldığı gibi $(cat ...), bir alt kabuğun çalışması için çatallanamaz ve bu nedenle büyük bir yükü önler.
Charles Duffy

4

Tek yapmanız gereken dosyayı arguments.txtiçerikle çevirmek

arg1
arg2
argN

içine my_command arg1 arg2 argNsadece kullanabilirsiniz xargs:

xargs -a arguments.txt my_command

Sen ek statik argümanlar koyabilirsiniz xargsgibi çağrı xargs -a arguments.txt my_command staticArghangi arayacakmy_command staticArg arg1 arg2 argN


1

Benim bash kabuğumda bir cazibe gibi çalıştı:

cat input_file | xargs -I % sh -c 'command1 %; command2 %; command3 %;'

girdi_dosyası

arg1
arg2
arg3

Açıkça görüldüğü gibi, input_file dosyasındaki her satırda birden fazla komut çalıştırmanıza izin verir, burada öğrendiğim güzel bir numara .


3
"Güzel küçük numara" nız güvenlik açısından tehlikelidir - içeren bir argümanınız varsa $(somecommand), o komutu metin olarak iletmek yerine çalıştırırsınız. Benzer şekilde, >/etc/passwdyeniden yönlendirme ve üzerine yazma /etc/passwd(uygun izinlerle çalıştırılırsa) vb. Olarak işlenecektir
Charles Duffy

2
Bunun yerine aşağıdakileri yapmak çok daha güvenlidir (GNU uzantılarına sahip bir sistemde): xargs -d $'\n' sh -c 'for arg; do command1 "$arg"; command2 "arg"; command3 "arg"; done' _- ve aynı zamanda daha etkilidir, çünkü giriş dosyanızdaki her satır için bir kabuk başlatmak yerine mümkün olduğunca çok sayıda argüman iletir.
Charles Duffy


0

@Wesley Rice'ın cevabını birkaç kez düzenledikten sonra , değişikliklerimin kendi cevabımı yazmak yerine cevabını değiştirmeye devam etmek için çok büyük olduğuna karar verdim. Ben de kendiminkini yazmam gerektiğine karar verdim!

Bir dosyanın her satırını okuyun ve üzerinde satır satır çalışın:

#!/bin/bash
input="/path/to/txt/file"
while IFS= read -r line
do
  echo "$line"
done < "$input"

Bu doğrudan yazar Vivek Gite'den geliyor: https://www.cyberciti.biz/faq/unix-howto-read-line-by-line-from-file/ . Krediyi alır!

Sözdizimi: Bash Unix ve Linux kabuğunda dosyayı satır satır okuyun:
1. Sözdizimi, bir dosyayı satır
2 okumak için bash, ksh, zsh ve diğer tüm kabuklar için aşağıdaki gibidir . while read -r line; do COMMAND; done < input.file
3. -rSeçenek, okuma komutuna geçti ters eğik çizgi kaçışlarının yorumlanmasını engeller.
4. IFS=Öndeki / arkadaki boşlukların kırpılmasını önlemek için okuma komutundan önce seçenek ekleyin -
5.while IFS= read -r line; do COMMAND_on $line; done < input.file


Ve şimdi de şu anda kapalı olan bu soruyu cevaplamak için: Bir dosyadaki dosyaların listesini `` git '' mümkün müdür? - işte cevabım:

FILES_STAGEDHer satırın yapmak istediğim bir dosyaya göreli bir yol olduğu bir grup satır içeren bir dosyanın mutlak yolunu içeren bir değişken olduğunu unutmayın git add. Bu kod parçacığı parçası olmak üzeredir "eRCaGuy_dotfiles / useful_scripts / sync_git_repo_to_build_machine.sh" başka (ex: Bir PC (bir bilgisayar ben kod ex): dan gelişiminde dosyaların kolay senkronizasyonu sağlamak için, bu projede dosyanın bir daha güçlü bir bilgisayar geliştiriyorum): https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles .

while IFS= read -r line
do
    echo "  git add \"$line\""
    git add "$line" 
done < "$FILES_STAGED"

Referanslar:

  1. Cevabımı nereden kopyaladığım: https://www.cyberciti.biz/faq/unix-howto-read-line-by-line-from-file/
  2. Döngü sözdizimi için: https://www.cyberciti.biz/faq/bash-for-loop/

İlişkili:

  1. Dosyanın içeriğini satır satır nasıl okuyabilir ve git addüzerinde nasıl çalışabilirim: Bir dosyadaki dosyaların listesini `` git '' mümkün müdür?
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.