2011-11-28 13 views
99

Rozważmy następujące polecenie:Ignoruj ​​pusty wynik dla xargs

ls /mydir/*.txt | xargs chown root 

zamiarem jest zmienić właściciele wszystkich plików tekstowych w mydir wykorzenić

Kwestia jest taka, że ​​jeśli nie ma .txt pliki w mydir następnie xargs pokazuje błąd informujący, że nie podano żadnej ścieżki. Jest to nieszkodliwy przykład, ponieważ zgłaszany jest błąd, ale w niektórych przypadkach, podobnie jak w skrypcie, którego potrzebuję tutaj użyć, zakłada się pustą ścieżkę do bieżącego katalogu. Więc jeśli uruchomię tę komendę z /home/tom/, jeśli nie ma wyniku dla ls /mydir/*.txt, a wszystkie pliki pod /home/tom/ mają ich właścicieli zmienionych na root.

Więc w jaki sposób mogę mieć xargs ignorować pusty wynik?

+4

bok: Nigdy wyjście rury z 'ls' dla programowej użytkowania; zobacz http://mywiki.wooledge.org/ParsingLs –

Odpowiedz

179

GNU xargs, można skorzystać z opcji -r lub --no-run-if-empty:

--no-run-if-empty
-r
Jeżeli standardowe wejście nie zawiera żadnych nonblanks, nie uruchomić komendę. Zwykle polecenie jest uruchamiane raz, nawet jeśli nie ma danych wejściowych. Ta opcja jest rozszerzeniem GNU.

+6

Szczerze szukałem w google jako pierwszy i znalazłem to (ale nie zapytałem bez przeczytania instrukcji). Myślę, że to powinno być zachowanie domyślne, nie wymagające przełącznika, aby to umożliwić. – JonnyJD

+20

Niestety to nie działa na komputerze Mac. Ani krótka, ani długa opcja. – wedi

+4

@wedi - OSX ma przestrzeń użytkownika BSD, więc tego typu rzeczy zdarzają się często. Możesz obejść go, używając homebrew, aby zainstalować pliki GNU. – edk750

10

mówi --no-run-if-empty.

+0

Jeśli jesteś na OSXie, nie będzie. edk750 wyjaśnił to w swoim komentarzu, jeśli jesteś ciekawy dlaczego. – jasonleonhard

7

Użytkownicy xargs non-gnu mogą skorzystać z -L <#lines>, -n <#args>, -i i -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 

Należy pamiętać, że xargs dzieli linię na spacji ale cytowanie i ucieczki są dostępne; RTFM dla szczegółów.

+0

Wygląda na to, że nie odpowiada na pytanie? –

+1

@Nicolas: jest to sposób na uniknięcie wykonywania, jeśli twoja wersja 'xargs' nie obsługuje przełącznika' --no-run-if-empty' i próbuje uruchomić argument polecenia bez wejścia STDIN (tak jest w Solarisie 10). Wersje w innych systemach uniksowych mogą po prostu ignorować pustą listę (np. AIX). – arielCo

+3

Tak! W systemie MAC OSX: 'echo '' | xargs -n1 echo bla "nic nie robi; natomiast "echo" x "| xargs -n1 echo bla "drukuje" blah ". – carlosayam

6

Pod względem , można użyć -r zgodnie z sugestią, jednak nie jest ona obsługiwana przez BSD xargs.

Więc jak obejście może przekazać jakieś dodatkowe tymczasowego pliku, na przykład:

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

lub przekierować jego stderr do wartości null (2> /dev/null), na przykład

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

Innym lepszym rozwiązaniem jest iteracyjne nad znalezionych plików za pomocą while pętlę:

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

Zobacz także: Ignore empty results for xargs in Mac OS X


Proszę również pamiętać, że metoda zmieniania uprawnienia nie są świetne i są odradzane. Zdecydowanie nie należy parsować danych wyjściowych ls command (patrz: Why you shouldn't parse the output of ls). Szczególnie, gdy uruchamiasz swoje polecenie przez root, ponieważ twoje pliki mogą składać się ze znaków specjalnych, które mogą być interpretowane przez powłokę lub wyobrazić sobie plik mający znak przestrzeni wokół /, wtedy wyniki mogą być straszne.

Dlatego należy zmienić swoje podejście i użyć zamiast tego polecenia , np.

find /mydir -type f -name "*.txt" -execdir chown root {} ';' 
+0

Niestety, nie rozumiem, w jaki sposób plik 'while IFS = read -r -d '' jest poprawny. Możesz wytłumaczyć? – Adrian

1

Na OSX: Bash reimplementacja xargs czynienia z -r argumentu, że na przykład umieścić w $HOME/bin i dodać go do 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 [email protected] 
Powiązane problemy