Leiten Sie die Ausgabe eines Befehls weiter, wenn dieser erfolgreich ist

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

Ich wollte den zweiten Befehl ( head -1) nur ausführen, wenn der erste Befehl erfolgreich ist. Wie verbessere ich diesen Befehl?

Govind Kailas
quelle
Was meinst du mit erfolgreich? lswird nicht scheitern.
Matteo
2
Der Glob kann jedoch fehlschlagen, wenn keine übereinstimmenden Dateien vorhanden sind.
Tripleee

Antworten:

8

Versuche dies:

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

Wenn Sie xargsdas -rFlag übergeben, wird es nur ausgeführt, basenamewenn mindestens ein Element aus der Standardeingabe ( head -1) gelesen wird .

head -1 wird ausgeführt, aber Sie werden keine Ausgabe davon sehen oder erfassen.

Wenn Sie nicht möchten, dass der Benutzer eine Fehlerausgabe von lssieht, können Sie den lsstderr-Stream an umleiten /dev/null.

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

Beachten Sie auch, dass ich Anführungszeichen hinzugefügt habe $MY_DIR. Auf diese Weise schlägt der Befehl nicht fehl, wenn er $MY_DIRLeerzeichen enthält.

Wenn Sie eine moderne Shell wie Bash verwenden, sollten Sie $( )anstelle von Backticks eine Capture-Shell verwenden. Sie sollten auch überlegen, den Stil Ihrer Variablen zu ändern. Sie sollten generell vermeiden, Variablennamen in Großbuchstaben in Skripten zu verwenden. Dieser Stil ist im Allgemeinen reservierten Variablen und Umgebungsvariablen vorbehalten.

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

quelle
Ist das nicht ein Problem, wenn es keine Dateien gibt?
Mel Boyce
Eigentlich: ls -rt GLOB 2>/dev/null | head -n1 | xargs -r basenamefunktioniert.
Mel Boyce
@ MelBoyce: Ich habe das zu meiner Antwort hinzugefügt. Vielen Dank.
ls ist ein Tool zum interaktiven Anzeigen von Dateiinformationen. Die Ausgabe ist für Menschen formatiert und führt zu Fehlern in Skripten. Verwenden Sie stattdessen Globs oder Find. Verstehen , warum: mywiki.wooledge.org/ParsingLs
cinelli
@cinelli: Das stimmt. Ich habe nur die Frage beantwortet.
9

In einer Pipeline werden alle Befehle gleichzeitig gestartet und nicht nacheinander ausgeführt. Sie müssen die Ausgabe also irgendwo speichern.

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

Beachten Sie, dass Dateinamen keine Zeilenumbrüche enthalten. Die Verwendung von xargswürde auch Probleme mit Leerzeichen, einfachen und doppelten Anführungszeichen und Backslashes bedeuten. Wenn Variablen nicht in Anführungszeichen gesetzt werden, treten Probleme mit Leerzeichen, Tabulatoren und Platzhalterzeichen auf. Vergessen --würde Probleme mit Dateinamen bedeuten, die mit beginnen -.

So erhalten Sie den Basisnamen der ältesten Datei zshohne eine dieser Zeichenbeschränkungen (umgeht auch das Problem der begrenzten Größe von Argumenten für einen Befehl):

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

Wenn es keine Übereinstimmung gibt, schlägt dieser Befehl fehl und bricht das Skript ab. Sie können auch:

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

In diesem Fall enthält das first_file_nameArray 1 Element, wenn eine Übereinstimmung vorliegt, oder 0, wenn nicht. Sie können dann Folgendes tun:

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

Suchen Sie die zuletzt geänderte Datei in einem Verzeichnis:

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"
}

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

basenameVerwenden Sie die Parametererweiterung, anstatt zu rufen .

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

In einem Verzeichnis mit diesen Inhalten:

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

Der Inhalt der Variable base_fn wäre: newest

So verwenden Sie dies ordnungsgemäß, um den Zweck Ihrer Anfrage zu erfüllen:

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

BEARBEITEN: Bei der Überprüfung der Frage habe ich festgestellt, dass der lsbetreffende Befehl nach der ältesten Datei in einem Verzeichnis sucht . Dieselbe Funktion könnte in umbenannt werden oldestund muss [[ $file -ot $oldest ]] && oldest=$filestattdessen den gleichen Effekt erzielen. Wir entschuldigen uns für etwaige Verwirrung.

Das Wichtigste ist, dass Sie auf keinen Fall die Ausgabe von ls analysieren sollten. noch nie.

Josh McGee
quelle
Beachten Sie, dass im Gegensatz zu den auf ls-basierten Ansätzen bei Symlinks die Änderungszeit des Ziels der Symlinks berücksichtigt wird (fügen Sie die -LOption hinzu ls, um dasselbe Verhalten dort zu erzielen).
Stéphane Chazelas
0

Ich denke, dass die beste Option hier ist:

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

Wenn es keine Datei gibt, ist der Rückgabecode von ls 2, wenn jedoch eine Datei gefunden wird, ist er 0.

BitsOfNix
quelle
2
Anstatt den Vergleich $?gegen 0, können Sie dies tun: if ls -rf $MY_DIR/FILE.*.xml ; then.
Außerdem können Sie die Ausgabe des ersten Befehls an umleiten /dev/null. ls -rf $MY_DIR/FILE.*.xml &> /dev/null. Auf diese Weise wird dem Benutzer die zusätzliche Ausgabe nicht angezeigt.