Pemrograman shell, menghindari tempfile

8

Saya sering menulis skrip shell KSH yang mengikuti pola yang sama:

  • (1) mengambil output dari satu atau lebih perintah
  • (2) format menggunakan grep | cut | awk | sed dan cetak ke layar atau ke file

Untuk melakukan itu, saya sering menyimpan output (1) di tempfile, dan kemudian melakukan format di (2) pada file itu.

Ambil kode itu misalnya:

TMPFILE=file.tmp

# If tmpfile exists rm it.
[ -f $TMPFILE ] && rm -f $TMPFILE

for SERVICE in $(myfunc); do
    getInfo $SERVICE > $TMPFILE # Store raw output in the TMPFILE

    # I retrieve the relevant data from the TMPFILE
    SERV_NAME=$(head -1 $TMPFILE | sed -e 's/ $//')
    SERV_HOSTNAME=$(grep HOSTNAME $TMPFILE | cut -d "=" -f2)
    SERV_ARGS=$(grep Arguments $TMPFILE | cut -d ":" -f2)

    print $SERV_NAME $SEP $SERV_HOSTNAME $SEP $SERV_ARGS
    rm -f $TMPFILE #rm the TMPFILE in vue of next iteration
done

Apakah ada cara, menggunakan pipa, pengalihan dan whatnots, untuk menghindari menulis file ke disk setiap kali?

Jika ini membantu, saya menggunakan ksh Versi M-11/16 / 88i

rahmu
sumber
3
Ini adalah bentuk yang baik untuk menghindari nama variabel ALL_CAPS dalam skrip shell, dan memperlakukan namespace tersebut sebagai dilindungi oleh shell untuk menghindari hal-hal penting seperti PATHatau variabel shell atau lingkungan lainnya. TMPFILEmungkin baik-baik saja, tetapi TMPDIRspesial, jadi apakah Anda benar-benar ingin berjalan di atas tali itu?
jw013
Untuk anak cucu: pertanyaan lain yang ditandai sebagai duplikat dari yang ini unix.stackexchange.com/questions/63923/... termasuk jawaban yang melibatkan pipa bernama fifo, yang juga dapat digunakan di sini (meskipun mungkin bukan pilihan terbaik di kasus khusus ini).
goldilocks
@goldilocks: Mungkin kita bisa menggabungkan dua pertanyaan menjadi satu. Bisakah kita menghubungi moderator untuk melakukan ini?
rahmu
@rahmu: Saya menandai pertanyaan lainnya. Saya kira itu tergantung pada kekuatan yang ada sekarang ...
goldilocks

Jawaban:

9

Kode Anda terlihat seperti contoh yang sepenuhnya dibenarkan menggunakan tempfile untuk saya. Saya akan tetap: tetap dengan pendekatan ini. Satu-satunya hal yang benar-benar perlu diubah adalah cara Anda membuat tempfile. Gunakan sesuatu seperti

 TMP=$(tempfile)

atau

 TMP=$(mktemp)

atau setidaknya

 TMP=/tmp/myscript_$$

Dengan cara ini Anda tidak akan membiarkan nama mudah diprediksi (keamanan) dan keluar campur tangan aturan antara beberapa contoh skrip berjalan pada saat yang sama.

rozcietrzewiacz
sumber
2
pedantically, kutipan tidak diperlukan untuk penugasan variabel.
glenn jackman
1
@glenn Benar, dalam hal ini mereka seharusnya tidak membuat perbedaan, karena setiap perintah biasanya menghasilkan string tanpa spasi. Tapi itu kebiasaan yang baik untuk memiliki tanda kutip dalam kasus di mana Anda menetapkan output perintah ke variabel - jadi saya akan tetap bertahan dengan cara ini.
rozcietrzewiacz
Menghapus kutipan dalam contoh terakhir untuk pembedaan.
rozcietrzewiacz
3
@roz Tidak, Anda melewatkan intinya. Penugasan variabel dalam shell dikenali sebelum ekspansi dilakukan, dan pemisahan bidang TIDAK dilakukan untuk penugasan variabel. Dengan demikian, var=$(echo lots of spaces); echo "$var"tidak masalah dan harus menghasilkan lots of spacessebagai output. Peringatan sebenarnya tidak ada yang disebutkan adalah perintah strip pengganti semua baris baru. Ini bukan masalah di sini, dan hanya masalah misalnya jika Anda memiliki patah mktempyang membuat nama file dengan mengikuti baris baru. Pekerjaan biasa di sekitar, jika diperlukan, adalah var=$(echo command with trailing newline; echo x); var=${var%x}.
jw013
1
@ jw013 Ya, saya menyadari ini sekarang - tidak, ketika saya menulis jawabannya setahun yang lalu. Terima kasih telah menunjukkannya! (memperbaiki ...)
rozcietrzewiacz
5

Anda bisa menggunakan variabel:

info="$(getInfo $SERVICE)"
SERV_NAME="$(head -1 $TMPFILE <<<"$info" | sed -e 's/ $//')"
...

Dari man ksh:

<<<word       A  short  form of here document in which word becomes the
              contents of the here-document after any parameter  expan-
              sion,  command  substitution, and arithmetic substitution
              occur.

Keuntungan meliputi:

  • Memungkinkan eksekusi paralel.
  • Dalam pengalaman saya, ini lebih cepat daripada file sementara. Kecuali jika Anda memiliki begitu banyak data yang akhirnya bertukar, itu seharusnya menjadi urutan besarnya lebih cepat (hanya melarang buffer caching HD, yang mungkin secepat untuk jumlah data kecil).
  • Proses atau pengguna lain tidak dapat mengacaukan data Anda.
l0b0
sumber
<<< sepertinya tidak ada di ksh saya. Saya mendapatkan kesalahan, dan sepertinya saya tidak dapat menemukannya di halaman manual. Saya menggunakan ksh88. Apakah Anda yakin versi ini harus memiliki fitur ini?
rahmu
Nggak; Saya kira saya tidak memeriksa manhalaman kanan (tidak disebutkan nomor versi di halaman web: /)
l0b0
<<<adalah bash 'here string'. Saya tidak berpikir itu muncul di shell lain. (Oh, zshmungkin ...)
rozcietrzewiacz
2
@rozcietrzewiacz: Google untuk man ksh. Itu pasti disebutkan di sana.
l0b0
3
Coba tebak bagaimana bash mengimplementasikan di sini-string dan di sini-docs. sleep 3 <<<"here string" & lsof -p $! | grep 0rsleep 30251 anthony 0r REG 253,0 12 263271 /tmp/sh-thd-7256597168 (deleted)- ya, ini menggunakan tempfile.
derobert
2

Anda memiliki dua opsi:

  1. Anda mengambil data satu kali (dalam contoh Anda dengan getInfo) dan menyimpannya dalam file seperti yang Anda lakukan.

  2. Anda mengambil data setiap kali dan tidak menyimpannya secara lokal, yaitu, Anda menelepon getInfosetiap waktu

Saya tidak melihat masalah dalam membuat file sementara untuk menghindari pemrosesan ulang / pengambilan ulang.

Jika Anda khawatir meninggalkan file sementara di suatu tempat, Anda selalu dapat menggunakan trapuntuk memastikan untuk menghapusnya jika skripnya terbunuh / terganggu

trap "rm -f $TMPFILE" EXIT HUP INT QUIT TERM

dan gunakan mktempuntuk membuat nama file unik untuk file sementara Anda.

Matteo
sumber
1

Alih-alih menghasilkan file, buat pernyataan penetapan shell dan evaluasi output itu.

for SERVICE in $(myfunc); do
    eval $(getInfo $SERVICE |
               sed -n -e '1/\(.*\) *$/SERV_NAME="\1"/p' \
                   -e '/HOSTNAME/s/^[^=]*=\([^=]*\).*/SERV_HOSTNAME="\1"/p' \
                   -e '/Arguments/^[^:]*:\([^:]*\).*/SERV_ARGS="\1"/p')
    print $SERV_NAME $SEP $SERV_HOSTNAME $SED $SERV_ARGS
done

Atau jika Anda hanya ingin mencetak informasi:

for SERVICE in $(myfunc); do
    getInfo $SERVICE | awk -vsep="$SEP" '
        BEGIN{OFS=sep}
        NR == 1 { sub(/ *$/,""); SERV_NAME=$0 }
        /HOSTNAME/ { split($0, HOST, /=/; SERV_HOSTNAME=HOST[2]; }
        /Arguments/ { split($0, ARGS, /:/; SERV_ARGS }
        END { print SERV_NAME, SERV_HOSTNAME, SERV_ARGS }'
done
Arcege
sumber