Bagaimana cara melewatkan setiap baris file teks sebagai argumen ke perintah?

42

Saya ingin menulis skrip yang menggunakan .txtnama file sebagai argumen, membaca file baris demi baris, dan melewati setiap baris ke sebuah perintah. Sebagai contoh, ini berjalan command --option "LINE 1", lalu command --option "LINE 2", dll. Output dari perintah ditulis ke file lain. Bagaimana cara saya melakukan itu? Saya tidak tahu harus mulai dari mana.

WojciechF
sumber

Jawaban:

26

Gunakan while readloop:

: > another_file  ## Truncate file.

while IFS= read -r LINE; do
    command --option "$LINE" >> another_file
done < file

Lain adalah untuk mengarahkan output dengan blokir:

while IFS= read -r LINE; do
    command --option "$LINE"
done < file > another_file

Terakhir adalah membuka file:

exec 4> another_file

while IFS= read -r LINE; do
    command --option "$LINE" >&4
    echo xyz  ## Another optional command that sends output to stdout.
done < file

Jika salah satu dari perintah membaca masukan, itu akan menjadi ide yang baik untuk menggunakan fd lain untuk input sehingga perintah tidak akan memakannya (di sini dengan asumsi ksh, zshatau bashuntuk -u 3, penggunaan <&3bukan portable):

while IFS= read -ru 3 LINE; do
    ...
done 3< file

Akhirnya untuk menerima argumen, Anda dapat melakukan:

#!/bin/bash

FILE=$1
ANOTHER_FILE=$2

exec 4> "$ANOTHER_FILE"

while IFS= read -ru 3 LINE; do
    command --option "$LINE" >&4
done 3< "$FILE"

Yang mana bisa dijalankan sebagai:

bash script.sh file another_file

Ide ekstra. Dengan bash, gunakan readarray:

readarray -t LINES < "$FILE"

for LINE in "${LINES[@]}"; do
    ...
done

Catatan: IFS=dapat dihilangkan jika Anda tidak keberatan memiliki nilai garis yang dipangkas dari spasi awal dan akhir.

konsolebox
sumber
28

Pilihan lainnya adalah xargs.

Dengan GNU xargs:

< file xargs -I{} -d'\n' command --option {} other args

{} adalah dudukan untuk baris teks.

Lainnya xargstidak memiliki -d, tetapi beberapa memiliki -0untuk input yang dibatasi oleh NUL. Dengan itu, Anda dapat melakukan:

< file tr '\n' '\0' | xargs -0 -I{} command --option {} other args

Pada sistem Unix-conformant ( -Ibersifat opsional dalam POSIX dan hanya diperlukan untuk sistem Unix-conformant), Anda harus memproses kembali input untuk mengutip garis-garis dalam format yang diharapkan oleh xargs:

< file sed 's/"/"\\""/g;s/.*/"&"/' |
  xargs -E '' -I{} command --option {} other args

Namun perhatikan bahwa beberapa xargsimplementasi memiliki batas yang sangat rendah pada ukuran maksimum argumen (255 pada Solaris misalnya, minimum yang diizinkan oleh spesifikasi Unix).

ctrl-alt-delor
sumber
dapat direduksi menjadi<file xargs -L 1 -I{} command --option {} other args
iruvar
@iruvar, tidak, itu akan memproses tanda kutip dan menghapus beberapa kekosongan.
Stéphane Chazelas
7

Menjaga dengan tepat pertanyaan:

#!/bin/bash

# xargs -n param sets how many lines from the input to send to the command

# Call command once per line
[[ -f $1 ]] && cat $1 | xargs -n1 command --option

# Call command with 2 lines as args, such as an openvpn password file
# [[ -f $1 ]] && cat $1 | xargs -n2 command --option

# Call command with all lines as args
# [[ -f $1 ]] && cat $1 | xargs command --option
miigotu
sumber
1
bekerja seperti pesona :-)
BugHunter
3

Jawaban terbaik yang saya temukan adalah:

for i in `cat`; do "$cmd" "$i"; done < $file

EDIT:

... empat tahun kemudian ...

setelah beberapa suara turun dan beberapa pengalaman lagi saya akan merekomendasikan sekarang yang berikut ini

xargs -l COMMAND < file
Steffen Roller
sumber
3
Ini akan memanggil perintah sekali untuk setiap kata dalam file. Selain itu, Anda harus selalu mengutip referensi ke variabel shell (seperti pada do "$cmd" "$i";) kecuali Anda memiliki alasan untuk tidak; jika file berisi kata *sebagai kata tersendiri, kode Anda akan berjalan $cmd *, yang, tentu saja, akan menjalankan perintah dengan daftar file di direktori saat ini.
G-Man Mengatakan 'Reinstate Monica'
1
@ G-Man, kecuali di zsh, `cat`sudah akan memperluas *(tanda kutip $imasih bisa memperluas beberapa wildcard (babak kedua) jika perluasan `cat`memperkenalkan beberapa). Bagaimanapun, pendekatan itu memang salah.
Stéphane Chazelas
1
+1. Ini akan berfungsi dengan baik, jika setiap baris hanya berisi input yang diperlukan untuk perintah yang digunakan tanpa spasi.
Subin Sebastian
Ini berlaku untuk setiap kata, adakah varian dari ini yang berlaku pada setiap baris?
thebunnyrules
Pertanyaannya adalah untuk memproses daftar nama file baris demi baris.
Steffen Roller
2
    sed "s/'/'\\\\''/g;s/.*/\$* '&'/" <<\FILE |\
    sh -s -- command echo --option
all of the{&}se li$n\es 'are safely shell
quoted and handed to command as its last argument
following --option, and, here, before that echo
FILE

KELUARAN

--option all of the{&}se li$n\es 'are safely shell
--option quoted and handed to command as its last argument
--option following --option, and, here, before that echo
mikeserv
sumber
-2
ed file.txt
%g/^/s// /
2,$g/^/-,.j
1s/^/command/
wq
chmod 755 file.txt
./file.txt

Ambil semua baris file dan berikan sebagai argumen untuk satu perintah, yaitu

command line1 line2 line3 ....

Jika Anda membutuhkan --optionbendera untuk mendahului setiap baris, ubah perintah kedua menjadi:

%g/^/s// --option /
pengguna2217522
sumber
1
Apa -1 untuk menggunakan ed ... sungguh?
user2217522
3
Saya tidak memilih Anda, tetapi orang yang melakukannya mungkin memiliki alasan-alasan berikut: (1) Ini tidak melakukan apa yang diminta pertanyaan. Perintah ini dijalankan sekali dengan semua isi file pada baris perintah; daripada sekali per baris . (2) Ini tidak melakukan apa-apa untuk menangani karakter yang khusus untuk shell (yang mungkin ada di file); misalnya, ', ", <, >, ;, dll (3) ini menciptakan sebuah file sementara yang tidak perlu. (4) Hal-hal seperti ini umumnya dilakukan dengan "dokumen di sini". (5) edPerintah Anda canggung; dua perintah pertama dapat dikurangi menjadi %s/^/ /dan %j.
G-Man Mengatakan 'Reinstate Monica'