Pipa output dari sebuah perintah jika berhasil

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

Saya ingin menjalankan perintah kedua ( head -1) hanya jika perintah pertama berhasil. Bagaimana cara meningkatkan perintah ini?

Govind Kailas
sumber
Apa yang Anda maksud dengan sukses? lstidak akan gagal.
Matteo
2
Tetapi glob bisa gagal jika tidak ada file yang cocok.
tripleee

Jawaban:

8

Coba ini:

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

Melewati xargssatu -rbendera akan menyebabkan ia hanya menjalankan basenamejika membaca setidaknya satu item dari input standar ( head -1).

head -1 akan berjalan tetapi Anda tidak akan melihat atau menangkap output apa pun darinya.

Juga, jika Anda tidak ingin pengguna melihat output kesalahan ls, Anda dapat mengarahkan ulang lsaliran stderr ke /dev/null.

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

Perhatikan juga bahwa saya menambahkan tanda kutip di sekitar $MY_DIR. Dengan begitu, perintah tidak akan gagal jika $MY_DIRberisi spasi.

Jika Anda menggunakan shell modern seperti bash, Anda harus menggunakan $( )shell ambil alih-alih backticks. Anda juga harus mempertimbangkan mengubah gaya variabel Anda. Anda umumnya harus menghindari menggunakan nama variabel huruf besar semua dalam skrip. Gaya itu umumnya dicadangkan untuk variabel cadangan dan lingkungan.

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

sumber
Bukankah ini b0rk jika tidak ada file?
Mel Boyce
Sebenarnya: ls -rt GLOB 2>/dev/null | head -n1 | xargs -r basenameberfungsi.
Mel Boyce
@MelBoyce: Saya telah menambahkan itu ke jawaban saya. Terima kasih.
ls adalah alat untuk melihat informasi file secara interaktif. Outputnya diformat untuk manusia dan akan menyebabkan bug pada skrip. Gunakan gumpalan atau temukan sebagai gantinya. Pahami mengapa: mywiki.wooledge.org/ParsingLs
cinelli
@cinelli: Itu benar. Saya hanya menjawab pertanyaan itu.
9

Dalam saluran pipa, semua perintah dimulai dan dijalankan secara bersamaan, bukan satu demi satu. Jadi, Anda perlu menyimpan output di suatu tempat.

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

Perhatikan bahwa ini mengasumsikan nama file tidak mengandung karakter baris baru. Menggunakan xargsjuga berarti masalah dengan karakter kosong, tanda kutip tunggal dan ganda dan garis miring terbalik. Meninggalkan variabel yang tidak dikutip akan berarti masalah dengan karakter spasi, tab dan karakter pengganti. Lupa --berarti masalah dengan nama file yang dimulai dengan -.

Untuk mendapatkan nama file dari file terlama dengan zshtanpa batasan karakter apa pun (juga menghindari masalah ukuran argumen terbatas pada perintah):

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

Jika tidak ada kecocokan, perintah itu akan gagal dan membatalkan skrip. Anda juga dapat melakukan:

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

Dalam hal ini first_file_namearray akan berisi 1 elemen jika ada kecocokan atau 0 jika tidak. Anda kemudian dapat melakukan:

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

Temukan file yang dimodifikasi terbaru dalam direktori:

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

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

Alih-alih memanggil basename, gunakan ekspansi parameter.

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

Dalam direktori dengan konten ini:

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

Isi variabel base_fn adalah: newest

Untuk menggunakan ini dengan benar untuk memenuhi tujuan permintaan Anda:

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: setelah meninjau pertanyaan, saya menyadari bahwa lsperintah tersebut mencari file tertua di direktori. fungsi yang sama ini dapat diubah namanya menjadi oldestdan memiliki [[ $file -ot $oldest ]] && oldest=$fileuntuk mencapai efek yang sama. permintaan maaf untuk kebingungan

hal penting yang perlu diperhatikan adalah bahwa Anda benar-benar, dalam keadaan apa pun dalam umat manusia, tidak boleh mem-parsing keluaran ls. tidak pernah.

Josh McGee
sumber
Perhatikan bahwa bertentangan dengan lspendekatan berbasis, jika ada symlink, waktu modifikasi target symlink akan dipertimbangkan (tambahkan -Lopsi lsuntuk mendapatkan perilaku yang sama di sana).
Stéphane Chazelas
0

Saya pikir pilihan terbaik di sini adalah:

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

jika tidak ada file maka kode pengembalian ls adalah 2 tetapi jika menemukan beberapa file akan menjadi 0.

BitsOfNix
sumber
2
Alih-alih membandingkan $?terhadap 0, Anda dapat melakukan ini: if ls -rf $MY_DIR/FILE.*.xml ; then.
Anda juga dapat mengarahkan output dari perintah pertama ke /dev/null. ls -rf $MY_DIR/FILE.*.xml &> /dev/null. Dengan begitu, pengguna tidak akan melihat output tambahan.