Potokuj wyjście polecenia, jeśli się powiedzie


14
INPUT_FILE=`ls -rt $MY_DIR/FILE.*.xml | head -1 | xargs basename`

Chciałem wykonać drugie polecenie ( head -1) tylko wtedy, gdy pierwsze polecenie zakończy się powodzeniem. Jak poprawić to polecenie?


Co rozumiesz przez odniesienie sukcesu? lsnie zawiedzie.
Matteo,

2
Ale glob może się nie powieść, jeśli nie ma pasujących plików.
tripleee

Odpowiedzi:


8

Spróbuj tego:

INPUT_FILE=`ls -rt "$MY_DIR"/FILE.*.xml | head -1 | xargs -r basename`

Przechodząc xargsdo -rflagi spowoduje to tylko uruchomić basenamejeśli czyta przynajmniej jeden element ze standardowego wejścia ( head -1).

head -1 będzie działać, ale nie zobaczysz ani nie wychwycisz z niego żadnych danych wyjściowych.

Ponadto, jeśli nie chcesz, aby użytkownik widział wynik wyjściowy błędu ls, możesz przekierować lsstrumień stderr do /dev/null.

INPUT_FILE=`ls -rt "$MY_DIR"/FILE.*.xml 2> /dev/null | head -1 | xargs -r basename`

Zauważ też, że dodałem znaki cudzysłowu $MY_DIR. W ten sposób polecenie nie zawiedzie, jeśli $MY_DIRzawiera spacje.

Jeśli używasz nowoczesnej powłoki, takiej jak bash, powinieneś użyć $( )powłoki przechwytującej zamiast backkicks. Powinieneś także rozważyć zmianę stylu swoich zmiennych. Zasadniczo należy unikać używania w skryptach nazw zmiennych składających się z wielkich liter. Ten styl jest ogólnie zarezerwowany dla zmiennych zarezerwowanych i zmiennych środowiskowych.

input_file=$(ls -rt "$my_dir"/FILE.*.xml 2> /dev/null | head -1 | xargs -r basename)

Czy to nie działa, jeśli nie ma plików?
Mel Boyce,

Właściwie: ls -rt GLOB 2>/dev/null | head -n1 | xargs -r basenamedziała.
Mel Boyce,

@MelBoyce: Dodałem to do mojej odpowiedzi. Dzięki.

ls to narzędzie do interaktywnego przeglądania informacji o plikach. Jego dane wyjściowe są sformatowane dla ludzi i będą powodować błędy w skryptach. Zamiast tego użyj globów lub znajdź. Dowiedz się,
cinelli

@cinelli: To prawda. Właśnie odpowiadałem na pytanie.

9

W linii potoku wszystkie polecenia są uruchamiane i uruchamiane jednocześnie, nie jedna po drugiej. Musisz gdzieś przechowywać dane wyjściowe.

if ls_output=$(ls -rtd -- "$MY_DIR"/FILE.*.xml); then
  first_file=$(printf '%s\n' "$ls_output" | head -n 1)
  first_file_name=$(basename -- "$first_file")
fi

Zauważ, że zakłada, że ​​nazwy plików nie zawierają znaków nowej linii. Używanie xargsoznaczałoby również problemy z pustymi znakami, pojedynczymi i podwójnymi cudzysłowami oraz odwrotnymi ukośnikami. Pozostawienie zmiennych bez cudzysłowu oznaczałoby problemy ze spacjami, tabulatorami i znakami wieloznacznymi. Zapominanie --oznaczałoby problemy z nazwami plików zaczynającymi się od -.

Aby uzyskać nazwę bazową najstarszego pliku zshbez żadnych ograniczeń dotyczących znaków (pozwala również uniknąć problemu ograniczonej wielkości argumentów polecenia):

 first_file_name=($MY_DIR/FILE.*.xml(Om[1]:t))

Jeśli nie ma dopasowania, to polecenie zakończy się niepowodzeniem i przerwie skrypt. Możesz także:

 first_file_name=($MY_DIR/FILE.*.xml(NOm[1]:t))

W takim przypadku first_file_nametablica będzie zawierać 1 element, jeśli występuje dopasowanie, lub 0, jeśli nie. Następnie możesz wykonać:

 if (($#first_file_name)); then
    printf 'Match: %s\n' $first_file_name
 else
    echo >&2 No match
 fi

4

Znajdź najnowszy zmodyfikowany plik w katalogu:

latest() {
  local file path=${1:-.} ext=${2-} latest
  for file in "${path%/}"/*"$ext"; do 
    [[ $file -nt $latest ]] && latest=$file
  done
  [[ $latest ]] && printf '%s\n' "$latest"
}

Stosowanie: latest [directory/path/ [.extension]]

Zamiast wołać do basename, użyj rozszerzenia parametrów.

in_file=$(latest in/my/dir .xml)
base_fn=${in_file##*/} base_fn=${base_fn%.*}

W katalogu z tymi treściami:

foo.xml bar.xml baz.xml newest.xml

Zawartość zmiennej base_fn będzie wynosić: newest

Aby właściwie to wykorzystać, aby zrealizować cel żądania:

check_dir=/path/to/check
check_ext=.xml
if in_file=$(latest "$check_dir" "$check_ext"); then
  base_fn=${in_file##*/} base_fn=${base_fn%.*}
else
  printf '%s\n' "No file found in $check_dir" >&2
fi

EDYCJA: po przejrzeniu pytania uświadomiłem sobie, że to lspolecenie szuka najstarszego pliku w katalogu. Ta sama funkcja może być zmieniona na oldesti mieć [[ $file -ot $oldest ]] && oldest=$filezamiast osiągnąć ten sam efekt. przepraszam za zamieszanie.

ważną rzeczą do zapamiętania jest to, że absolutnie, pod żadnym pozorem w ludzkości, nie powinieneś analizować wyników ls. nigdy.


Zauważ, że w przeciwieństwie do lsmetod opartych na bazach, jeśli istnieją dowiązania symboliczne, rozważony zostanie czas modyfikacji celu dowiązań symbolicznych (dodaj -Lopcję lsuzyskania takiego samego zachowania).
Stéphane Chazelas

0

Myślę, że najlepszą opcją jest tutaj:

ls -rf $MY_DIR/FILE.*.xml
if [ $? -eq 0 ]; then
      INPUT_FILE=`ls -rt $MY_DIR/FILE.*.xml|head -1|xargs basename `
else
      echo "Error!"
fi

jeśli nie ma pliku, kod powrotu ls to 2, ale jeśli znajdzie jakiś plik, będzie miał wartość 0.


2
Zamiast porównywać $?Against 0, można to zrobić: if ls -rf $MY_DIR/FILE.*.xml ; then.

Możesz także przekierować wyjście pierwszego polecenia /dev/null. ls -rf $MY_DIR/FILE.*.xml &> /dev/null. W ten sposób użytkownik nie zobaczy dodatkowych danych wyjściowych.
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.