Bagaimana cara mengabaikan perintah xargs jika input stdin kosong?

189

Pertimbangkan perintah ini:

ls /mydir/*.txt | xargs chown root

Tujuannya adalah mengubah pemilik semua file teks mydirmenjadi root

Masalahnya adalah jika tidak ada .txtfile di dalam mydirxargs maka muncul kesalahan yang mengatakan tidak ada path yang ditentukan. Ini adalah contoh yang tidak berbahaya karena kesalahan sedang terjadi, tetapi dalam beberapa kasus, seperti dalam skrip yang perlu saya gunakan di sini, jalan kosong diasumsikan sebagai direktori saat ini. Jadi jika saya menjalankan perintah itu dari /home/tom/maka jika tidak ada hasil untuk ls /mydir/*.txtdan semua file di bawah /home/tom/memiliki pemiliknya diubah menjadi root.

Jadi bagaimana saya bisa membiarkan xargs mengabaikan hasil kosong?

HyderA
sumber
6
Selain itu: Jangan pernah menyalurkan output dari lsuntuk penggunaan program; lihat mywiki.wooledge.org/ParsingLs
Charles Duffy
git branch --merged | grep -v '^* ' | xargs git branch -d
Kasing

Jawaban:

305

Untuk GNU xargs, Anda dapat menggunakan opsi -ratau --no-run-if-empty:

--no-run-if-empty
-r
Jika input standar tidak mengandung nonblank, jangan jalankan perintah. Biasanya, perintah dijalankan sekali walaupun tidak ada input. Opsi ini adalah ekstensi GNU.

Sven Marnach
sumber
9
Saya jujur ​​mencari di google pertama dan menemukan ini (tetapi tidak akan bertanya tanpa membaca manual). Saya agak berpikir ini akan menjadi perilaku default, tidak perlu beralih untuk mengaktifkannya.
JonnyJD
28
Sayangnya ini tidak berfungsi pada Mac. Baik opsi pendek maupun panjang.
wedi
9
@wedi - OSX memiliki ruang pengguna BSD, jadi hal seperti ini akan sering terjadi. Anda dapat mengatasinya dengan menggunakan homebrew untuk menginstal fileutils GNU.
edk750
7
Maksudmu brew install findutils. findutils, Tidak fileutils.
Mitar
3
Pada macOS, tidak menyediakan flag mengakibatkan tidak menjalankan perintah, jadi saya kira itu tidak diperlukan.
Trejkaz
15

Pengguna xargs non-GNU dapat mengambil keuntungan dari -L <#lines>, -n <#args>, -i, dan -I <string>:

ls /empty_dir/ | xargs -n10 chown root # chown executed every 10 args or fewer
ls /empty_dir/ | xargs -L10 chown root # chown executed every 10 lines or fewer
ls /empty_dir/ | xargs -i cp {} {}.bak # every {} is replaced with the args from one input line
ls /empty_dir/ | xargs -I ARG cp ARG ARG.bak # like -i, with a user-specified placeholder

Perlu diingat bahwa xargs membagi garis di whitespace tetapi mengutip dan melarikan diri tersedia; RTFM untuk detail.

Juga, seperti yang disebutkan Doron Behar, solusi ini tidak portabel sehingga pemeriksaan mungkin diperlukan:

$ uname -is
SunOS sun4v
$ xargs -n1 echo blah < /dev/null
$ uname -is
Linux x86_64
$ xargs --version | head -1
xargs (GNU findutils) 4.7.0-git
$ xargs -n1 echo blah < /dev/null
blah
arielCo
sumber
2
Sepertinya tidak menjawab pertanyaan?
Nicolas Raoul
2
@ Nicolas: ini adalah cara untuk mencegah eksekusi jika versi xargsAnda tidak mendukung --no-run-if-emptysakelar dan mencoba menjalankan argumen perintah tanpa input STDIN (ini adalah kasus di Solaris 10). Versi di unix lain mungkin mengabaikan daftar kosong (mis. AIX).
arielCo
4
Iya! Pada MAC OSX, echo '' | xargs -n1 echo blahtidak melakukan apa-apa; sedangkan echo 'x' | xargs -n1 echo blahmencetak "bla".
carlosayam
2
Untuk dukungan GNU dan BSD / OSX saya akhirnya menggunakan sesuatu seperti:, seperti yang ls /mydir/*.txt | xargs -n 1 -I {} chown root {}disarankan oleh jawaban ini.
Luís Bianchin
3
Perlu dicatat bahwa echo '' | xargs -n1 echo blahcetakan blahdengan GNU xargs.
Doron Behar
11

man xargskata --no-run-if-empty.

thiton
sumber
1
Jika Anda menggunakan OSX tidak akan. edk750 telah menjelaskan hal ini dalam komentar mereka jika Anda penasaran mengapa.
jasonleonhard
9

Dalam hal xargs, Anda dapat menggunakan -rseperti yang disarankan, namun itu tidak didukung oleh BSDxargs .

Jadi sebagai solusi Anda dapat melewati beberapa file sementara tambahan, misalnya:

find /mydir -type f -name "*.txt" -print0 | xargs -0 chown root $(mktemp)

atau mengarahkan ulang stderrnya ke null ( 2> /dev/null), mis

find /mydir -type f -name "*.txt" -print0 | xargs -0 chown root 2> /dev/null || true

Pendekatan lain yang lebih baik adalah beralih pada file yang ditemukan menggunakan whileloop:

find /mydir -type f -name "*.txt" -print0 | while IFS= read -r -d '' file; do
  chown -v root "$file"
done

Lihat juga: Abaikan hasil kosong untuk xargs di Mac OS X


Harap perhatikan juga bahwa metode Anda untuk mengubah izin tidak terlalu bagus dan tidak disarankan. Tentunya Anda tidak harus menguraikan output lsperintah (lihat: Mengapa Anda tidak harus menguraikan output ls ). Terutama ketika Anda menjalankan perintah Anda dengan root, karena file Anda dapat terdiri dari karakter khusus yang dapat ditafsirkan oleh shell atau membayangkan file memiliki karakter spasi di sekitar /, maka hasilnya bisa mengerikan.

Karena itu Anda harus mengubah pendekatan Anda dan menggunakan findperintah, mis

find /mydir -type f -name "*.txt" -execdir chown root {} ';'
kenorb
sumber
1
Maaf, saya tidak mengerti bagaimana while IFS= read -r -d '' filevalid. Bisakah Anda jelaskan?
Adrian
2

Di OSX: Bash implementasi ulang xargsberurusan dengan -rargumen, masukkan misalnya dalam $HOME/bindan tambahkan ke PATH:

#!/bin/bash
stdin=$(cat <&0)
if [[ $1 == "-r" ]] || [[ $1 == "--no-run-if-empty" ]]
then
    # shift the arguments to get rid of the "-r" that is not valid on OSX
    shift
    # wc -l return some whitespaces, let's get rid of them with tr
    linecount=$(echo $stdin | grep -v "^$" | wc -l | tr -d '[:space:]') 
    if [ "x$linecount" = "x0" ]
    then
      exit 0
    fi
fi

# grep returns an error code for no matching lines, so only activate error checks from here
set -e
set -o pipefail
echo $stdin | /usr/bin/xargs $@
rcomblen
sumber
2

Ini adalah perilaku GNU xargs yang dapat ditekan dengan menggunakan -r, --no-run-if-empty.

Varian * BSD xargs memiliki behavoir ini secara default, jadi -r tidak diperlukan. Sejak FreeBSD 7.1 (dirilis pada Januari 2009) argumen -r diterima (penyihir tidak melakukan apa-apa) untuk alasan kompatiblity.

Saya pribadi lebih suka menggunakan longopts dalam skrip tetapi karena * BSD xargs tidak menggunakan longopts cukup gunakan "-r" dan xargs akan bertindak sama pada * BSD dan pada sistem linux

xargs pada MacOS (saat ini MacOS Mojave) sayangnya tidak mendukung argumen "-r".

Mirko Steiner
sumber