Installa l'output di un comando se ha esito positivo


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

Volevo eseguire il secondo comando ( head -1) solo se il primo comando ha esito positivo. Come posso migliorare questo comando?


Cosa intendi con successo? lsnon fallirà.
Matteo,

2
Ma il glob può fallire se non ci sono file corrispondenti.
Tripleee,

Risposte:


8

Prova questo:

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

Passare xargsil -rflag lo farà funzionare solo basenamese legge almeno un elemento dallo standard input ( head -1).

head -1 verrà eseguito ma non vedrai né catturerai alcun output da esso.

Inoltre, se non si desidera che l'utente visualizzi alcun output degli errori ls, è possibile reindirizzare lsil flusso stderr a /dev/null.

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

Si noti inoltre che ho aggiunto virgolette in giro $MY_DIR. In questo modo, il comando non fallirà se $MY_DIRcontiene spazi.

Se stai usando una shell moderna come bash, dovresti usare una $( )shell di cattura invece di backtick. Dovresti anche considerare di cambiare lo stile delle tue variabili. Dovresti generalmente evitare di usare nomi di variabili maiuscoli negli script. Tale stile è generalmente riservato a variabili riservate e ambientali.

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

Questo non funziona se non ci sono file?
Mel Boyce,

In realtà: ls -rt GLOB 2>/dev/null | head -n1 | xargs -r basenamefunziona.
Mel Boyce,

@MelBoyce: l'ho aggiunto alla mia risposta. Grazie.

ls è uno strumento per guardare interattivamente le informazioni sui file. Il suo output è formattato per gli umani e causerà bug negli script. Usa globs o trova invece. Capire perché: mywiki.wooledge.org/ParsingLs
Cinelli

@cinelli: è vero. Stavo solo rispondendo alla domanda.

9

In una linea di condotte, tutti i comandi vengono avviati ed eseguiti contemporaneamente, non uno dopo l'altro. Quindi è necessario memorizzare l'output da qualche parte.

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

Si noti che presuppone che i nomi dei file non contengano caratteri di nuova riga. L'uso xargssignificherebbe anche problemi con caratteri vuoti, virgolette singole e doppie e barre rovesciate. Lasciare le variabili non quotate significherebbe problemi con spazi, tab e caratteri jolly. Dimenticare --significherebbe problemi con i nomi dei file a partire da -.

Per ottenere il nome di base del file più vecchio zshsenza nessuna di quelle restrizioni sui caratteri (evita anche il problema della dimensione limitata degli argomenti di un comando):

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

Se non c'è corrispondenza, quel comando fallirà e interromperà lo script. Puoi anche fare:

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

Nel qual caso l' first_file_namearray conterrà 1 elemento in caso di corrispondenza o 0 in caso contrario. È quindi possibile fare:

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

4

Trova l'ultimo file modificato in una directory:

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

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

Invece di chiamare basename, utilizzare l'espansione dei parametri.

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

In una directory con questi contenuti:

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

Il contenuto della variabile base_fn sarebbe: newest

Per utilizzarlo correttamente per soddisfare lo scopo della tua richiesta:

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

EDIT: dopo aver esaminato la domanda, mi sono reso conto che il lscomando in questione sta cercando il file più vecchio in una directory. questa stessa funzione potrebbe essere rinominata oldeste avere [[ $file -ot $oldest ]] && oldest=$fileinvece per ottenere lo stesso effetto. scuse per qualsiasi confusione.

la cosa importante da notare è che tu, assolutamente in nessun caso nell'umanità, non dovresti analizzare l'output di ls. mai.


Si noti che contrariamente agli lsapprocci basati su, se ci sono collegamenti simbolici, verrà considerato il tempo di modifica della destinazione dei collegamenti simbolici (aggiungere l' -Lopzione lsper ottenere lo stesso comportamento lì).
Stéphane Chazelas,

0

Penso che l'opzione migliore qui sia:

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

se non è presente alcun file, il codice di ritorno di ls è 2 ma se trova un file sarà 0.


2
Invece di confrontare $?contro 0, si può fare questo: if ls -rf $MY_DIR/FILE.*.xml ; then.

Inoltre, puoi reindirizzare l'output del primo comando su /dev/null. ls -rf $MY_DIR/FILE.*.xml &> /dev/null. In questo modo, l'utente non vedrà l'output aggiuntivo.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.