Jalankan dua perintah pada satu argumen (tanpa skrip)


28

Bagaimana saya bisa melakukan dua perintah pada satu input, tanpa mengetik input itu dua kali?

Sebagai contoh, perintah stat memberi tahu banyak tentang file, tetapi tidak menunjukkan jenis file:

stat fileName

Perintah file, memberi tahu tipe file apa itu:

file fileName

Anda dapat melakukan ini dalam satu baris dengan cara ini:

stat fileName ; file fileName

Namun, Anda harus mengetikkan nama file dua kali.

Bagaimana Anda bisa menjalankan kedua perintah pada input yang sama (tanpa mengetik input atau variabel input dua kali)?

Di Linux, saya tahu cara mem-pipe output, tapi bagaimana Anda mem-pipe input?


14
Untuk lebih jelasnya, itu bukan "input", itu argumen (alias parameter). Input dapat disalurkan melalui <(input dari file ke sisi kiri) atau |(input dari stream ke sisi kanan). Ada perbedaan.
goldilocks

6
Bukan jawaban untuk pertanyaan Anda, tetapi mungkin jawaban untuk masalah Anda: Dalam bash, yank-last-argperintah (pintasan default Meta-.atau Meta-_; Metasering menjadi tombol kiri Alt) akan menyalin parameter terakhir dari baris perintah sebelumnya ke yang sekarang. Jadi, setelah mengeksekusi stat fileNameAnda mengetik file [Meta-.]dan tidak perlu mengetik fileNamelagi. Saya selalu menggunakannya.
Dubu

2
Dan perbedaannya adalah bahwa <dan >adalah pengalihan , tidak piping . Sebuah pipa menghubungkan dua proses, sementara pengalihan hanya menetapkan ulang stdin / stdout.
bsd

@ rolldown: Ya, tapi Anda mengarahkan pipa meskipun bukan. Anda dapat mengarahkan ulang pipa untuk membaca dari file pada disk, atau Anda dapat mengarahkan ulang pipa untuk menulis ke file pada disk. Masih piping. ;-)
James Haigh

Jawaban:


22

Ini cara lain:

$ stat filename && file "$_"

Contoh:

$ stat /usr/bin/yum && file "$_"
  File: ‘/usr/bin/yum’
  Size: 801         Blocks: 8          IO Block: 4096   regular file
Device: 804h/2052d  Inode: 1189124     Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Context: system_u:object_r:rpm_exec_t:s0
Access: 2014-06-11 22:55:53.783586098 +0700
Modify: 2014-05-22 16:49:35.000000000 +0700
Change: 2014-06-11 19:15:30.047017844 +0700
 Birth: -
/usr/bin/yum: Python script, ASCII text executable

Itu bekerja di bashdan zsh. Itu juga berfungsi di mkshdan dashtetapi hanya jika interaktif. Di AT&T ksh, itu hanya berfungsi ketika file "$_"ada pada jalur yang berbeda dari yang statada.


1
Yang !$tidak akan bekerja karena !$memperluas kata terakhir dari peristiwa sejarah terakhir (jadi dari baris perintah yang dimasukkan sebelumnya, bukan dari perintah stat).
Stéphane Chazelas

@ StéphaneChazelas: Oh, ya, kesalahan saya, saya menghapusnya.
cuonglm

32

Mungkin akan membuat buku-buku jari saya mengetuk untuk ini, berikut adalah kombinasi yang aneh dari ekspansi bash brace dan eval yang tampaknya melakukan trik

eval {stat,file}" fileName;"


2
ekspansi brace berasal csh, bukan bash. bashmenyalinnya dari ksh. Kode itu akan bekerja di semua csh, tcsh, ksh, zsh, fish dan bash.
Stéphane Chazelas

1
Sayang sekali Anda kehilangan kemampuan untuk "keluar" (lengkapi-otomatis) nama file, karena kutipan.
Lonniebiz

Maaf, tapi saya tidak bisa menahan baik
tomd

maaf apa masalahnya di sini eval? (Mengacu pada komentar teratas)
user13107

28

Dengan zsh, Anda dapat menggunakan fungsi anonim:

(){stat $1; file $1} filename

Dengan eslambdas:

@{stat $1; file $1} filename

Anda juga bisa:

{ stat -; file -;} < filename

(melakukan yang statpertama sebagai fileakan memperbarui waktu akses).

Saya akan melakukan:

f=filename; stat "$f"; file "$f"

Namun, untuk itulah variabelnya.


3
{ stat -; file -;} < filenameadalah sihir. statharus memeriksa untuk melihat apakah stdin-nya terhubung ke file dan melaporkan atribut file? Agaknya tidak memajukan pointer pada stdin karena itu memungkinkan fileuntuk mengendus isi stdin
iruvar

1
@ 1_CR, ya. stat -memberitahu statuntuk melakukan fstat()pada fd 0.
Stéphane Chazelas

4
The { $COMMAND_A -; $COMMAND_B; } < filenamevarian tidak akan bekerja dalam kasus umum jika $ COMMAND_A benar-benar membaca masukan apapun; mis. { cat -; cat -; } < filenameakan mencetak isi nama file hanya sekali.
Max Nanasy

2
stat -ditangani secara khusus sejak coreutils-8.0 (Okt 2009), dalam versi sebelum itu Anda akan mendapatkan kesalahan (kecuali jika Anda memiliki file yang dipanggil -dari percobaan sebelumnya, dalam hal ini Anda akan mendapatkan hasil yang salah ...)
mr .spuratic

1
@ mr.spuratic. Ya, dan statistik BSD, IRIX, dan zsh juga tidak akan mengenalinya. Dengan stat zsh dan IRIX, Anda dapat menggunakannya stat -f0sebagai gantinya.
Stéphane Chazelas

9

Semua jawaban ini tampak seperti scripting bagi saya, pada tingkat yang lebih besar atau lebih kecil. Untuk solusi yang benar-benar tidak melibatkan skrip, dan berasumsi bahwa Anda sedang duduk di keyboard mengetikkan shell, Anda dapat mencoba:

Enter
file stat somefile Esc_   Enter

Dalam setidaknya bash, zsh, dan ksh, dalam mode vi dan emacs, menekan Escape dan kemudian menggarisbawahi memasukkan kata terakhir dari baris sebelumnya ke dalam buffer edit untuk baris saat ini.

Atau, Anda dapat menggunakan substitusi riwayat:

Enter
file stat somefile ! $Enter

Dalam bash, zsh, csh, tcsh, dan kebanyakan shell lainnya, !$diganti dengan kata terakhir dari baris perintah sebelumnya ketika baris perintah saat ini diuraikan.


opsi apa yang tersedia di zsh / bash seperti apa Esc _? Bisakah Anda terhubung dengan dokumentasi resmi?
Abhijeet Rastogi


1
zsh: linux.die.net/man/1/zshzle dan cari "insert-last-word"
godlygeek

1
@shadyabhi ^ Tautan untuk keybindings standar bash dan zsh. Perhatikan bahwa bash hanya menggunakan readline, jadi (sebagian besar) bash akan bekerja di aplikasi apa pun yang menggunakan pustaka readline.
godlygeek

3

Cara yang saya temukan untuk melakukan ini cukup sederhana adalah dengan menggunakan xargs, dibutuhkan file / pipa dan mengubah isinya menjadi argumen program. Ini dapat dikombinasikan dengan tee yang membagi aliran dan mengirimkannya ke dua program atau lebih, Jika Anda membutuhkan:

echo filename | tee >(xargs stat) >(xargs file) | cat

Tidak seperti banyak jawaban lain, ini akan bekerja di bash dan kebanyakan shell lain di Linux. Saya akan menyarankan ini adalah kasus penggunaan variabel yang baik tetapi jika Anda benar-benar tidak dapat menggunakannya, ini adalah cara terbaik untuk melakukannya hanya dengan pipa dan utilitas sederhana (dengan asumsi Anda tidak memiliki shell yang sangat mewah).

Selain itu Anda dapat melakukan ini untuk sejumlah program dengan hanya menambahkannya dengan cara yang sama seperti kedua setelah tee.

EDIT

Seperti yang disarankan dalam komentar ada beberapa kelemahan dalam jawaban ini, yang pertama adalah potensi interlacing output. Ini dapat diperbaiki seperti:

echo filename | tee >(xargs stat) >(( wait $!; xargs file )) | cat

Ini akan memaksa perintah untuk dijalankan secara bergantian dan output tidak akan pernah terjalin.

Masalah kedua adalah menghindari proses substitusi yang tidak tersedia di beberapa shell, ini dapat dicapai dengan menggunakan tpipe bukan tee sebagai berikut:

echo filename | tpipe 'xargs stat' | ( wait $!; xargs file )

Ini harus portabel untuk hampir semua shell (saya harap) dan menyelesaikan masalah di memuji lainnya, namun saya menulis yang terakhir ini dari memori karena sistem saya saat ini tidak memiliki tpipe tersedia.


1
Substitusi proses adalah fitur ksh yang hanya berfungsi di ksh(bukan varian domain publik), bashdan zsh. Perhatikan itu statdan fileakan berjalan secara bersamaan, jadi mungkin output mereka akan terjalin (saya kira Anda menggunakan | catuntuk memaksa buffering output untuk membatasi efek itu?).
Stéphane Chazelas

@ StéphaneChazelas Anda benar tentang kucing, ketika menggunakannya saya belum pernah berhasil menghasilkan kasus input interlaced saat menggunakannya. Namun saya akan mencoba sesuatu untuk menghasilkan solusi yang lebih portabel sebentar.
Vality

3

Jawaban ini mirip dengan beberapa yang lain:

nama file stat   && file! #: 1

Berbeda dengan jawaban lain, yang secara otomatis mengulang argumen terakhir dari perintah sebelumnya, yang ini mengharuskan Anda untuk menghitung:

ls -ld nama file   && file! #: 2

Tidak seperti beberapa jawaban lain, ini tidak memerlukan penawaran:

stat "useless cat"  &&  file !#:1

atau

echo Sometimes a '$cigar' is just a !#:3.

2

Ini mirip dengan beberapa jawaban lain, tetapi lebih portabel daripada yang ini dan lebih sederhana dari yang ini :

sh -c 'stat "$ 0"; file "$ 0" ' nama file

atau

sh -c 'stat "$ 1"; file "$ 1" dengan nama file

di mana shditugaskan untuk $0. Meskipun tiga karakter lebih banyak untuk diketik, bentuk (kedua) ini lebih disukai karena itulah $0nama yang Anda berikan pada skrip inline itu. Ini digunakan, misalnya, dalam pesan kesalahan oleh shell untuk skrip itu, seperti ketika fileatau stattidak dapat ditemukan atau tidak dapat dieksekusi karena alasan apa pun.


Jika Anda ingin melakukannya

nama file  stat 1 nama file 2  nama file 3 …; file nama  file 1 nama file 2  nama file 3 ...

untuk jumlah file yang berubah-ubah, lakukan

sh -c 'stat "$ @"; file "$ @" 'sh filename 1  filename 2  filename 3

yang bekerja karena "$@"setara dengan "$1" "$2" "$3" ….


Itu $0adalah nama yang ingin Anda berikan pada skrip inline itu. Ini digunakan misalnya dalam pesan kesalahan oleh shell untuk skrip itu. Seperti kapan fileatau stattidak dapat ditemukan atau tidak dapat dieksekusi karena alasan apa pun. Jadi, Anda menginginkan sesuatu yang bermakna di sini. Jika tidak terinspirasi, gunakan saja sh.
Stéphane Chazelas

1

Saya pikir Anda mengajukan pertanyaan yang salah, setidaknya untuk apa yang Anda coba lakukan. statdan fileperintah mengambil parameter yang merupakan nama file dan bukan isinya. Ini kemudian pada perintah yang membaca file yang diidentifikasi oleh nama yang Anda tentukan.

Sebuah pipa seharusnya memiliki dua ujung, satu digunakan sebagai input dan satu lagi sebagai output dan ini masuk akal karena Anda mungkin perlu melakukan pemrosesan yang berbeda di sepanjang jalan dengan alat yang berbeda sampai Anda mendapatkan hasil yang dibutuhkan. Mengatakan bahwa Anda tahu cara mem-pipe output tetapi tidak tahu bagaimana mem-pipe input tidak benar secara prinsip.

Dalam kasus Anda, Anda tidak perlu pipa sama sekali karena Anda harus memberikan input dalam bentuk nama file. Dan bahkan jika alat-alat itu ( statdan file) akan membaca stdin, pipa tidak relevan di sini lagi karena input tidak boleh diubah oleh salah satu alat ketika sampai ke yang kedua.


1

Ini harus bekerja untuk semua nama file di direktori saat ini.

sh -c "$(printf 'stat -- "$1";file -- "$1";shift%.0b\n' *)" -- *

1
Dengan asumsi nama file tidak mengandung double quote, backslash, dollar, backtick, }... karakter dan jangan mulai dengan -. Beberapa printfimplementasi (zsh, bash, ksh93) memiliki a %q. Dengan zsh, itu bisa menjadi:printf 'stat %q; file %1$q\n' ./* | zsh
Stéphane Chazelas

@StephaneChezales - semua itu sudah diperbaiki sekarang kecuali untuk kemungkinan hardquote. Saya bisa menggunakan itu dengan satu sed s///kurasa, tetapi ini tentang ruang lingkup - dan jika orang memasukkan itu dalam nama file mereka, mereka harus menggunakan windows.
mikeserv

@ StéphaneChazelas - Nevermind - Saya lupa betapa mudahnya menangani itu.
mikeserv

Sebenarnya, ini bukan jawaban untuk pertanyaan seperti yang diajukan: "Bagaimana Anda bisa menjalankan kedua perintah pada input yang sama ( tanpa mengetik input ... dua kali )?" (Penekanan ditambahkan). Jawaban Anda berlaku untuk semua file di direktori saat ini - dan itu termasuk *dua kali . Anda mungkin juga berkata stat -- *; file -- *.
Scott

@Scott - input tidak diketik dua kali - *diperluas. The ekspansi dari *ditambah dua perintah untuk setiap argumen dalam * adalah input. Lihat? printf commands\ %s\;shift *
mikeserv

1

Ada beberapa referensi berbeda untuk 'input' di sini, jadi saya akan memberikan beberapa skenario dengan memahaminya terlebih dahulu. Untuk jawaban cepat Anda atas pertanyaan dalam bentuk terpendek :

stat testfile < <($1)> outputfile

Di atas akan melakukan stat pada testfile, ambil (redirect) itu STDOUT dan memasukkan itu ke dalam fungsi khusus berikutnya (bagian <()) kemudian output hasil akhir dari apa pun itu, ke dalam file baru (outputfile). File dipanggil, lalu direferensikan dengan bash built-in ($ 1 setiap kali setelah, sampai Anda memulai set instruksi baru).

Pertanyaan Anda luar biasa, dan ada beberapa jawaban dan cara untuk melakukan ini, tetapi memang berubah dengan apa yang Anda lakukan secara spesifik.

Misalnya, Anda dapat mengulanginya juga, yang cukup berguna. Penggunaan umum dari ini adalah, dalam pola pikir psuedo-code, adalah:

run program < <($output_from_program)> my_own.log

Mengambil dan memperluas pengetahuan itu memungkinkan Anda untuk menciptakan hal-hal seperti:

ls -A; (while read line; do printf "\e[1;31mFound a file\e[0m: $line\n"; done) < <(/bin/grep thatword * | /usr/bin/tee -a files_that_matched_thatword)

Ini akan melakukan ls -A sederhana pada direktori Anda saat ini, kemudian memberi tahu sementara untuk mengulang setiap hasil dari ls -A ke (dan di sinilah rumit!) Grep "thatword" di masing-masing hasil tersebut, dan hanya melakukan yang sebelumnya printf (merah) jika benar-benar menemukan file dengan "kata itu" di dalamnya. Ini juga akan mencatat hasil grep ke file teks baru, files_that_matched_thatword.

Contoh output:

ls -A; (while read line; do printf "\e[1;31mFound a file\e[0m: $line\n"; done) < <(/bin/grep thatword * | /usr/bin/tee -a files_that_matched_thatword)

index.html

Semua itu hanya mencetak hasil ls -A, tidak ada yang istimewa. Tambahkan sesuatu untuk diambil kali ini:

echo "thatword" >> newfile

Sekarang jalankan kembali:

ls -A; (while read line; do printf "\e[1;31mFound a file\e[0m: $line\n"; done) < <(/bin/grep thatword * | /usr/bin/tee -a files_that_matched_thatword)

files_that_matched_thatword  index.html  newfile

Found a file: newfile:thatword

Meskipun mungkin jawaban yang lebih melelahkan daripada yang Anda cari saat ini, saya percaya menyimpan catatan berguna seperti ini akan lebih bermanfaat bagi Anda di masa depan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.