Cara mendapatkan PIPESTATUS dan hasil dalam skrip bash

9

Saya mencoba untuk mendapatkan tanggal modifikasi terakhir dari file dengan perintah ini

TM_LOCAL=`ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'`

TM_LOCAL memiliki nilai seperti "2012-05-16 23:18" setelah eksekusi baris ini

Saya juga ingin memeriksa PIPESTATUS untuk melihat apakah ada kesalahan. Misalnya jika file tidak ada, lsmengembalikan 2. Tetapi $?memiliki nilai 0 karena memiliki nilai pengembalian sebesar awk.

Jika saya menjalankan perintah ini sendirian, saya dapat memeriksa nilai pengembalian ls dengan melihat ${PIPESTATUS[0]}

ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'

Tetapi $PIPESTATUStidak berfungsi seperti yang saya harapkan jika saya menetapkan output ke variabel seperti pada contoh pertama. Dalam hal ini, $PIPESTATUSarray hanya memiliki 1 elemen yang sama dengan$?

Jadi, pertanyaannya adalah, bagaimana saya bisa mendapatkan keduanya $PIPESTATUSdan menetapkan output ke variabel secara bersamaan?

Mustafa Serdar Şanlı
sumber

Jawaban:

8

Anda bisa melakukan ini:

TM_LOCAL=$(ls -l --time-style=long-iso ~/.vimrc | \
             awk '{ print $6" "$7 }' ; exit ${PIPESTATUS[0]} )

Maka $?akan menjadi kode kembali dari ls. Ini tidak berfungsi jika Anda memerlukan kode pengembalian dari lebih dari satu bagian pipa (tetapi Anda dapat membagi pipa jika hasilnya tidak terlalu besar, seperti di sini).

Inilah cara yang agak mahal untuk mendapatkan PIPESTATUSarray lengkap dan hasilnya. Tidak terlalu elegan, tetapi belum menemukan yang lain:

result=$(echo -e "a\nb\nc" | \
          ( cat ; exit 1 ) | \
          ( cat ; exit 42 ) ; echo ${PIPESTATUS[@]})
output=$(head -n -1 <<< "$result")
status=($(tail -n 1 <<< "$result"))
echo "Output:"
echo "$output"
echo "Results:"
echo "${status[@]}"

Pemberian yang mana:

Output:
a
b
c
Results:
0 1 42
Tikar
sumber
Ini berfungsi dalam kasus saya, tapi saya masih penasaran apakah ada cara untuk mendapatkan array pipestatus lengkap dan hasilnya.
Mustafa Serdar Şanlı
3

Gunakan set -o pipefaildi bashuntuk mendapatkan kode keluar paling kanan bukan nol dalam urutan perintah pipa sebagai $?. Dari man bash:

Jika diatur, nilai kembali dari sebuah pipa adalah nilai dari perintah terakhir (paling kanan) untuk keluar dengan status bukan-nol, atau nol jika semua perintah dalam pipa keluar dengan sukses. Opsi ini dinonaktifkan secara default.

Maka Anda cukup mengakses $?. Gunakan set +o pipefailuntuk menonaktifkan lagi.

Daniel Beck
sumber
2

Saya berasumsi masalahnya di sini adalah bahwa PIPESTATUS hilang secara keseluruhan segera setelah Anda menjalankan perintah. Anda bisa mendapatkan array PIPESTATUS lengkap di bash versi 2 atau lebih tinggi dengan cara ini:

declare -a status
status=(${PIPESTATUS[@]})

Kemudian akses ${status[0]}, ${status[1]}dll

eewanco
sumber
2

Masalah utama dengan "apa yang Anda harapkan" adalah bahwa sebuah perintah dalam backquotes dieksekusi dalam sebuah subkulit; $PIPESTATUSada di sana dan status yang dikembalikan dari saya mengikuti aturan yang sama seperti jika Anda mengeksekusi satu executable (atau skrip shell). Status perintah backquote adalah status paling kanan ( awk).

Untuk mengimplementasikan apa yang dikatakan @ Daniel Beck , setel pipefailopsi dalam subkulit dengan demikian:

TM_LOCAL=`set -o pipefail; ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'` sekarang status yang disimpan $?setelahnya akan menjadi status ls(jika bukan nol).

Namun, saya pikir if [ -f ~/.vimrc ];tes eksplisit ... akan lebih mudah dibaca.

Anda tidak bisa mendapatkan output ke variabel dan PIPESTATUSdikembalikan tanpa file sementara untuk yang pertama, atau menyusun yang terakhir menjadi string.

toddkaufmann
sumber
0

Saya ingin mengirim email hanya jika status keluarnya tidak nol

Triknya adalah untuk mendapatkan stdin untuk akhir pipa, Anda harus meletakkannya dalam subkulit - tetapi itu tampaknya menyembunyikan nilai PIPESTATUS ...

test cron mengeluarkan beberapa output dan keluar dengan 1 atau 0 ..

./testcron | (test ${PIPESTATUS[0]} -ne 0 || mail -s "testcron output" paul)

UPDATE: PIPESTATUS tidak terlihat sampai perintah pipa diproses

Paul Davey
sumber
0

Satu pilihan adalah untuk memeriksa keberadaan file Anda sebelum mendapatkan waktu modifikasi dengan panggilan ke stat. Karena statmengembalikan sedikit lebih banyak daripada yang Anda inginkan di cap waktu, Anda dapat memotongnya menggunakan ekspansi parameter.

Dengan GNU stat(misalnya di Linux), Anda dapat menjalankan:

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -c '%y' ~/.vimrc 2>/dev/null)
TM_LOCAL=${TM_LOCAL%:*}  # Safe to do, even if stat fails

Pada Mac OS X dan sistem BSD lainnya, statsintaks berbeda dan dapat menentukan format waktu:

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -f '%Sm' -t '%Y-%m-%d %H:%M' ~/.vimrc 2>/dev/null)
chepner
sumber
Dalam apa yang sekarang menjadi jawaban GNU, Anda mengatakan perubahan ke $TM_LOCALaman. Ini hanya aman jika Anda mengharapkannya tidak memiliki nilai sebelumnya. Katakan nilainya sebelumnya 2020-02-27 17:14dan tidak ada ~/.vimrcfile. Anda kemudian akan memilikinya 2020-02-27 17. Karena itu saya akan rantai dua baris bersama-sama dengan tambahan &&atau (lebih disukai karena itu tidak begitu terbaca) menggunakan ifbait.
Adam Katz