Bagaimana cara menangkap stdin ke variabel tanpa menghapus baris baru yang tertinggal?

9

Dalam skrip shell ...

Bagaimana cara menangkap stdin ke variabel tanpa menghapus baris baru yang tertinggal?

Saat ini saya sudah mencoba:

var=`cat`
var=`tee`
var=$(tee)

Dalam semua kasus $vartidak akan memiliki baris baru dari aliran input. Terima kasih.

JUGA: Jika tidak ada baris baru di input, maka solusinya tidak harus menambahkan satu .

DIPERBARUI DALAM CAHAYA DARI JAWABAN YANG DITERIMA:

Solusi terakhir yang saya gunakan dalam kode saya adalah sebagai berikut:

function filter() {
    #do lots of sed operations
    #see https://github.com/gistya/expandr for full code
}

GIT_INPUT=`cat; echo x`
FILTERED_OUTPUT=$(printf '%s' "$GIT_INPUT" | filter)
FILTERED_OUTPUT=${FILTERED_OUTPUT%x}
printf '%s' "$FILTERED_OUTPUT"

Jika Anda ingin melihat kode lengkapnya, silakan lihat halaman github untuk expandr , sebuah skrip shell filter-ekspansi git kata kunci open source yang saya kembangkan untuk tujuan keamanan informasi. Menurut aturan yang diatur dalam file .gitattributes (yang bisa spesifik cabang) dan git config , git mem -pipe setiap file melalui skrip shell Expandr.sh setiap kali mengecek masuk atau keluar dari repositori. (Itulah sebabnya sangat penting untuk menjaga jalur baru yang tertinggal, atau kekurangannya.) Ini memungkinkan Anda membersihkan informasi sensitif, dan bertukar dalam set nilai lingkungan yang berbeda untuk pengujian, pementasan, dan cabang langsung.

CommaToast
sumber
apa yang Anda lakukan di sini tidak perlu. filterDibutuhkan stdin- itu berjalan sed. Anda menangkap stdindi $GIT_INPUTkemudian mencetak yang kembali ke stdoutatas pipa untuk filterdan menangkap nya stdoutdi $FILTERED_OUTPUTkemudian mencetaknya kembali ke stdout. Semua 4 baris di bagian bawah contoh di atas bisa diganti dengan hanya ini: filter. Jangan tersinggung di sini, hanya saja ... Anda bekerja terlalu keras. Anda tidak perlu variabel shell sebagian besar waktu - hanya mengarahkan input ke tempat yang tepat dan meneruskannya.
mikeserv
Tidak, apa yang saya lakukan di sini diperlukan karena jika saya hanya melakukannya filter, maka itu akan menambahkan karakter baris baru ke ujung setiap aliran input yang awalnya tidak berakhir di baris baru. Sebenarnya saya awalnya hanya melakukan filtertetapi mengalami masalah yang menyebabkan saya untuk solusi ini karena tidak "selalu menambahkan baris baru" atau "selalu strip baris baru" adalah solusi yang dapat diterima.
CommaToast
sedmungkin akan melakukan baris baru ekstra - tetapi Anda harus mengatasinya filterdengan yang lain. Dan semua fungsi yang Anda miliki pada dasarnya melakukan hal yang sama - a sed s///. Anda menggunakan shell untuk mem-pipe data yang telah disimpannya di memorinya sedsehingga seddapat menggantikan data itu dengan data lain yang telah disimpan shell dalam memorinya sehingga seddapat menyalurkannya kembali ke shell. Kenapa tidak adil [ "$var" = "$condition" ] && var=new_value? Saya juga tidak mendapatkan array - apakah Anda menyimpan nama array di [0]kemudian gunakan seduntuk menggantikannya dengan nilai [1]? Mungkin mengobrol?
mikeserv
@ mikeserv - Apa manfaat memindahkan kode itu ke dalam filter? Ini berfungsi dengan baik apa adanya. Mengenai cara kerja kode di tautan saya dan mengapa saya mengaturnya seperti yang saya lakukan, ya, mari kita bicarakan di ruang obrolan.
CommaToast

Jawaban:

7

Baris baru yang tertinggal dilucuti sebelum nilai disimpan dalam variabel. Anda mungkin ingin melakukan sesuatu seperti:

var=`cat; echo x`

dan gunakan ${var%x}sebagai gantinya $var. Contohnya:

printf "%s" "${var%x}"

Perhatikan bahwa ini menyelesaikan masalah baris baru, tetapi bukan byte nol (jika input standar bukan teks), karena menurut substitusi perintah POSIX :

Jika output berisi byte nol, perilaku tidak ditentukan.

Tetapi implementasi shell dapat mempertahankan byte nol.

vinc17
sumber
Apakah file teks biasanya berisi byte nol? Saya tidak mengerti mengapa mereka melakukannya. Tetapi skrip yang baru saja Anda sebutkan tampaknya tidak berfungsi.
CommaToast
@CommaToast File teks tidak mengandung byte nol. Tetapi pertanyaannya hanya mengatakan stdin / input stream, yang mungkin bukan teks dalam kasus yang paling umum.
vinc17
BAIK. Yah saya mencobanya dari baris perintah dan tidak melakukan apa-apa, dan dari dalam skrip saya sendiri, saran Anda gagal karena itu menambahkan "..." di akhir file. Juga jika tidak ada baris baru di sana, maka itu masih menambahkan satu.
CommaToast
@CommaToast The "..." hanyalah sebuah contoh. Saya sudah mengklarifikasi jawaban saya. Tidak ada baris baru yang ditambahkan (lihat teks sebelum "..." pada contoh).
vinc17
1
Nah, kerang seharusnya tidak menyembunyikan hal-hal, itu tidak keren. Kerang itu harus ditembakkan. Saya tidak suka ketika komputer saya berpikir itu lebih baik dari saya.
CommaToast
4

Anda dapat menggunakan readbawaan untuk melakukan ini:

$ IFS='' read -d '' -r foo < <(echo bar)

$ echo "<$foo>"
<bar
>

Agar skrip membaca STDIN, itu hanya akan:

IFS='' read -d '' -r foo

 

Saya tidak yakin apa kerang ini akan bekerja. Tapi berfungsi dengan baik di bash dan zsh.

Patrick
sumber
Baik -dsubtitusi proses ( <(...)) tidak portabel; kode ini tidak akan berfungsi dash, misalnya.
chepner
Nah proses substitusi bukan bagian dari jawaban, itu hanya sebagian dari contoh yang menunjukkan bahwa ia berfungsi. Adapun -d, itu sebabnya saya menempatkan disclaimer di bagian bawah. OP tidak menentukan shell.
Patrick
@ chepner - walaupun gayanya sedikit berbeda, konsepnya tentu berhasil dash. Anda hanya menggunakan <<HEREDOC\n$(gen input)\nHEREDOC\n- in dash- yang menggunakan pipa untuk heredocs dengan cara yang sama dengan shell lain yang menggunakannya untuk proses substitusi - tidak ada bedanya. The read -dhal hanya menentukan pembatas - Anda dapat melakukan selusin cara yang sama - hanya menjadi yakin tentang hal itu. Meskipun Anda perlu ekor gen input.
mikeserv
Anda mengatur IFS = '' sehingga tidak menempatkan spasi di antara baris yang dibacanya eh? Trik keren.
CommaToast
Sebenarnya dalam hal ini IFS=''mungkin tidak perlu. Ini dimaksudkan agar readtidak membuat ruang kosong. Tetapi ketika sedang membaca ke dalam satu variabel, itu tidak memiliki efek (yang saya ingat). Tapi saya merasa lebih aman meninggalkannya pada :-)
Patrick
2

Anda dapat melakukannya seperti:

input | { var=$(sed '$s/$/./'); var=${var%.}; }

Apa pun yang Anda lakukan $varmenghilang begitu Anda keluar dari { current shell ; }pengelompokan itu. Tetapi bisa juga berfungsi seperti:

var=$(input | sed '$s/$/./'); var=${var%.}
mikeserv
sumber
1
Perlu dicatat bahwa dengan solusi pertama, yaitu harus digunakan $vardalam { ... }pengelompokan, tidak selalu mungkin. Sebagai contoh jika perintah ini dijalankan di dalam loop dan seseorang perlu di $varluar loop.
vinc17
@ vinc17 - jika itu adalah lingkaran yang saya inginkan untuk menggunakan, maka saya akan menggunakannya di tempat {}kawat gigi .Ini adalah benar - dan secara eksplisit dicatat dalam jawabannya - bahwa nilai untuk $varini sangat mungkin menghilang sepenuhnya bila { current shell; }pengelompokan adalah Tutup. Apakah ada cara yang lebih eksplisit untuk mengatakannya daripada, Apa pun yang Anda lakukan $varmenghilang ...?
mikeserv
@ vinc17 - mungkin cara terbaik, meskipun:input | sed "s/'"'/&"&"&/g;s/.*/process2 '"'-> &'/" | sh
mikeserv
1
Ada juga _variablesfungsi bash_completion, yang menyimpan hasil substitusi perintah dalam variabel global COMPREPLY. Jika solusi saluran pipa digunakan untuk menjaga saluran baru, hasilnya akan hilang. Dalam jawaban Anda, orang memiliki kesan bahwa kedua solusi sama baiknya. Selain itu harus dicatat bahwa perilaku solusi pipa sangat tergantung pada shell: pengguna dapat menguji echo foo | { var=$(sed '$s/$/./'); var=${var%.}; } ; echo $vardengan ksh93 dan zsh, dan berpikir bahwa itu OK, sedangkan kode ini bermasalah.
vinc17
1
Anda tidak mengatakan "itu tidak berhasil". Anda baru saja mengatakan " $varmenghilang" (yang sebenarnya tidak benar karena ini tergantung pada shell - perilaku tidak ditentukan oleh POSIX), yang merupakan kalimat yang agak netral. Solusi kedua lebih baik karena tidak menderita masalah ini, dan perilakunya konsisten di semua shell POSIX.
vinc17