Bagaimana cara menerapkan perintah shell ke setiap baris output perintah?

192

Misalkan saya memiliki beberapa output dari perintah (seperti ls -1):

a
b
c
d
e
...

Saya ingin menerapkan perintah (katakanlah echo) untuk masing-masing, pada gilirannya. Misalnya

echo a
echo b
echo c
echo d
echo e
...

Apa cara termudah untuk melakukannya di bash?

Alex Budovski
sumber
3
ls -1mungkin menjadi contoh di sini tetapi penting untuk diingat bahwa itu tidak baik untuk mengurai output dari ls. Lihat: mywiki.wooledge.org/ParsingLs
codeforester

Jawaban:

217

Mungkin paling mudah digunakan xargs. Dalam kasus Anda:

ls -1 | xargs -L1 echo

The -Lbendera memastikan input dibaca dengan benar. Dari halaman manual dari xargs:

-L number
    Call utility for every number non-empty lines read. 
    A line ending with a space continues to the next non-empty line. [...]
Michael Mrozek
sumber
24
lssecara otomatis tidak -1dalam pipa.
Dijeda sampai pemberitahuan lebih lanjut.
5
@ Dennis, tidak terlihat seperti itu: ls | xargs -L2 echodan ls -1 | xargs -L2 echomemberikan dua output yang berbeda. Yang pertama semuanya dalam satu baris.
Alex Budovski
2
@ Alex: Saya mendapatkan output yang sama.
Dijeda sampai pemberitahuan lebih lanjut.
6
xargshanya dapat menjalankan file yang dapat dieksekusi bukan fungsi shell atau perintah built-in shell. Untuk yang pertama solusi terbaik mungkin adalah yang ada readdalam satu lingkaran.
pabouk
12
Saya berharap jawaban ini termasuk penjelasan untuk apa -L1.
Wyck
164

Anda dapat menggunakan operasi dasar awal di setiap baris:

ls -1 | while read line ; do echo $line ; done

Atau Anda dapat menyalurkan output ke sed untuk operasi yang lebih kompleks:

ls -1 | sed 's/^\(.*\)$/echo \1/'
Trey Hunner
sumber
1
Perintah sed tampaknya tidak berfungsi: sh: cho: not found a sh: cho: not foundSepertinya itu mengambil egema in menjadi perintah sed atau sesuatu.
Alex Budovski
+1 untuk whileloop. cmd1 | while read line; do cmd2 $line; done. Atau while read line; do cmd2 $line; done < <(cmd1)yang tidak membuat subkulit. Ini adalah versi sederhana dari sedperintah Anda :sed 's/.*/echo &/'
Dijeda hingga pemberitahuan lebih lanjut.
1
@Alex: ubah tanda kutip ganda menjadi tanda kutip tunggal.
Dijeda sampai pemberitahuan lebih lanjut.
5
Mengutip "$line"di loop sementara, untuk menghindari pemisahan kata.
ignis
3
Coba gunakan read -r lineuntuk mencegah readbermain-main dengan karakter yang kabur. Misalnya echo '"a \"nested\" quote"' | while read line; do echo "$line"; donememberi "a "nested" quote", yang telah hilang melarikan diri. Jika kita melakukannya, echo '"a \"nested\" quote"' | while read -r line; do echo "$line"; donekita mendapatkan "a \"nested\" quote"seperti yang diharapkan. Lihat wiki.bash-hackers.org/commands/builtin/read
Warbo
9

Anda bisa menggunakan for for :

untuk file dalam *; melakukan
   gema "$ file"
selesai

Perhatikan bahwa jika perintah yang dimaksud menerima beberapa argumen, maka menggunakan xargs hampir selalu lebih efisien karena hanya perlu menelurkan utilitas yang dipermasalahkan sekali daripada beberapa kali.

Michael Aaron Safyan
sumber
1
Ada baiknya menggambarkan penggunaan xargs yang benar / aman, yaitu. printf '%s\0' * | xargs -0 ...- Jika tidak, itu tidak aman dengan nama file dengan spasi, tanda kutip, dll.
Charles Duffy
8

Anda sebenarnya dapat menggunakan sed untuk melakukannya, asalkan GNU sed.

... | sed 's/match/command \0/e'

Bagaimana itu bekerja:

  1. Pengganti cocok dengan pertandingan perintah
  2. Pada substitusi, jalankan perintah
  3. Ganti baris yang diganti dengan output perintah.
Łukasz Daniluk
sumber
Fantastis. Saya lupa untuk menempatkan perintah e di bagian akhir, tetapi melakukannya setelah melihat balasan Anda dan itu berhasil. Saya mencoba menambahkan ID acak antara 1000 dan 15000, ketika SED cocok dengan garis. cat /logs/lfa/Modified.trace.log.20150904.pw | sed -r 's/^(.*)(\|006\|00032\|)(.*)$/echo "\1\2\3 - ID `shuf -i 999-14999 -n 1`"/e'
sgsi
2

xargs gagal dengan backslash, tanda kutip. Itu perlu sesuatu seperti

ls -1 |tr \\n \\0 |xargs -0 -iTHIS echo "THIS is a file."

Opsi xargs -0:

-0, --null
          Input  items are terminated by a null character instead of by whitespace, and the quotes and backslash are
          not special (every character is taken literally).  Disables the end of file string, which is treated  like
          any  other argument.  Useful when input items might contain white space, quote marks, or backslashes.  The
          GNU find -print0 option produces input suitable for this mode.

ls -1menghentikan item dengan karakter baris baru, jadi trmenerjemahkannya menjadi karakter nol.

Pendekatan ini sekitar 50 kali lebih lambat daripada iterasi secara manual dengan for ...(lihat jawaban Michael Aaron Safyan ) (3,55s vs 0,066s). Tetapi untuk perintah input lain seperti mencari, menemukan, membaca dari file ( tr \\n \\0 <file) atau sejenisnya, Anda harus bekerja dengan xargsseperti ini.

phil294
sumber
1

Hasil yang lebih baik untuk saya:

ls -1 | xargs -L1 -d "\n" CMD
Andrej Pandovich
sumber
Lebih baik, tapi tidak sempurna. find . -mindepth 1 -maxdepth 1 -print0 | xargs -0 commandakan menangani kasus di mana output dari ls -1ambigu; gunakan -printf '%P\0'daripada -print0jika Anda tidak ingin memimpin ./pada masing-masing.
Charles Duffy
1

saya suka menggunakan gawk untuk menjalankan banyak perintah pada daftar, misalnya

ls -l | gawk '{system("/path/to/cmd.sh "$1)}'

Namun melarikan diri dari karakter yang lolos bisa menjadi sedikit berbulu.

Chris
sumber