Bagaimana cara mencetak string yang dipisahkan oleh TAB di bash?

9

Saya mencoba mencetak dua string yang dipisahkan oleh TAB. Saya telah mencoba:

echo -e 'foo\tbar'
printf '%s\t%s\n' foo bar

Keduanya mencetak:

foo     bar

Di mana spasi putih di antara keduanya sebenarnya 5 ruang (sesuai memilih output dengan mouse di Putty).

Saya juga telah mencoba menggunakan CTRL + V dan menekan TAB saat mengetik perintah, dengan hasil yang sama.

Apa cara yang benar untuk memaksa tab dicetak sebagai tab, jadi saya dapat memilih output dan menyalinnya ke tempat lain, dengan tab?

Dan pertanyaan kedua: mengapa bash memperluas tab ke spasi?

Pembaruan : Rupanya, ini adalah masalah Putty: /superuser/656838/how-to-make-putty-display-tabs-within-a-file-instead-of-changing-them-to -ruang

Asu
sumber
Mengapa tidak menghindarinya? printf '%s\\t%s\n' foo bar
Valentin Bajrami
@steeldriver Terima kasih itu sangat mirip dengan yang saya butuhkan, tetapi pada akhirnya tidak ada solusi ...
Asu
1
@Valentin Output itu foo\tbar...
wjandrea
1
Terlepas dari kenyataan bahwa Anda sudah tahu bahwa Anda memiliki masalah dengan terminal Anda: Bash dengan sendirinya menerjemahkan $'\t'sebagai tabulator. Jadi, Anda selalu dapat menggabungkan string seperti ini - misalnya sebagai tugas: v='This is'$'\t''a test'Dan mencetaknya secara harfiah, misalnyaprintf '%s' "$v"
rexkogitans

Jawaban:

9

Seperti kata ilkkachu, ini bukan masalah dengan bash, tetapi dengan emulator terminal yang mengubah tab menjadi spasi pada output.

Memeriksa terminal yang berbeda, dempul, xterm, dan konsole mengubah tab menjadi spasi, sedangkan urxvt dan gnome-terminal tidak. Jadi, solusi lain adalah mengganti terminal.

JoL
sumber
3
Ini juga dapat dilakukan oleh pengemudi tty setelah Anda menjalankan stty tab3.
Stéphane Chazelas
14

spasi putih di antara keduanya sebenarnya 5 ruang.

Tidak, tidak. Tidak dalam output echoatau printf.

$ echo -e 'foo\tbar' | od -c
0000000   f   o   o  \t   b   a   r  \n
0000010

Apa cara yang benar untuk memaksa tab dicetak sebagai tab, jadi saya dapat memilih output dan menyalinnya ke tempat lain, dengan tab?

Ini masalah yang berbeda. Ini bukan tentang shell tetapi terminal emulator, yang mengubah tab menjadi spasi pada output. Banyak, tetapi tidak semuanya melakukannya.

Mungkin lebih mudah untuk mengarahkan ulang output dengan tab ke file, dan menyalinnya dari sana, atau menggunakan unexpandoutput untuk mengkonversi spasi ke tab. (Meskipun itu juga tidak bisa tahu spasi apa itu tab untuk memulai, dan akan mengubahnya semua menjadi tab, jika mungkin.) Ini tentu saja tergantung pada apa, tepatnya, yang perlu Anda lakukan dengan output.

ilkkachu
sumber
Maksud saya ketika saya mencoba untuk memilih output, itu diperlakukan sebagai 5 spasi. Terima kasih untuk 'od -c' untuk memverifikasi isi output perintah.
Asu
1
@ Asu, saya pikir dia mengerti itu. Solusinya adalah mendapatkan output melalui cara lain karena emulator terminal tidak dijamin untuk meninggalkan tab sebagai tab ketika Anda memilihnya di jendela. Namun, saya baru saja memeriksa dan sementara dempul, xterm, dan konsole mengkonversi tab menjadi spasi, urxvt dan gnome-terminal tidak. Jadi, solusi lain adalah mengganti terminal.
JoL
@ JoL Ya, itulah kesimpulan saya baru saja semenit yang lalu, dan saya pikir itu akan menjadi jawaban yang diterima jika seseorang peduli untuk mempostingnya seperti itu ...
Asu
1
@Asu, ya, saya berpikir untuk mengatasi masalah ini secara manual. Akan sangat menyebalkan harus melakukan itu, tapi kemudian saya akui saya tidak menyadari bahwa ada emulator terminal yang mendukung tab penyalinan. Mengubah ke yang tidak, tentu saja akan menjadi solusi yang jauh lebih baik!
ilkkachu
4

Dalam printf '%s\t%s\n' foo bar, printfapakah output foo<TAB>bar<LF>.

f, o, b, aDan rkarakter grafis single-lebar.

Setelah menerima karakter-karakter itu, terminal akan menampilkan mesin terbang yang sesuai dan memindahkan kursor satu kolom ke kanan, kecuali itu sudah mencapai tepi kanan layar (kertas di mesin ketik tel-asli)), dalam hal ini mungkin memberi makan garis dan kembali ke tepi kiri layar (bungkus) atau hanya membuang karakter tergantung pada terminal dan bagaimana itu dikonfigurasi.

<Tab>dan <LF>dua karakter kontrol . <LF>(alias baris baru) adalah pembatas baris dalam teks Unix, tetapi untuk terminal, itu hanya memberi makan garis (pindahkan kursor satu posisi ke bawah). Jadi driver terminal di kernel akan benar-benar menerjemahkannya <CR>(kembali ke tepi kiri layar), <LF>(kursor ke bawah) ( stty onlcrumumnya aktif secara default).

<Tab> memberitahu terminal untuk memindahkan kursor ke perhentian tab berikutnya (yang pada kebanyakan terminal terpisah 8 posisi secara default tetapi juga dapat dikonfigurasi untuk diatur di mana saja) tanpa mengisi celah dengan kosong.

Jadi, jika karakter-karakter tersebut dikirim ke terminal dengan tab berhenti setiap 8 kolom sementara kursor berada di awal baris kosong, itu akan menghasilkan:

foo     bar

dicetak pada layar di garis itu. Jika dikirim saat kursor berada di posisi ketiga dalam baris yang berisi xxxxyyyyzzzz, itu akan menghasilkan:

xxfooyyybarz

Pada terminal yang tidak mendukung tabulasi, driver terminal dapat dikonfigurasi untuk menerjemahkan tab tersebut ke urutan spasi. ( stty tab3).

Karakter SPC, dalam mesin ketik tele asli akan memindahkan kursor ke kanan, sedangkan backspace ( \b) akan memindahkannya ke kiri. Sekarang di terminal modern, SPC bergerak ke kanan dan juga menghapus (menulis karakter ruang seperti yang Anda harapkan). Jadi liontin \bharus menjadi sesuatu yang lebih baru daripada ASCII. Pada kebanyakan terminal modern, itu sebenarnya urutan karakter: <Esc>, [, C.

Ada lebih banyak urutan pelarian untuk memindahkan nkarakter ke kiri, kanan, atas, bawah atau pada posisi apa pun di layar. Ada urutan pelarian lain untuk menghapus (mengisi dengan kosong) bagian dari garis atau wilayah layar, dll.

Urutan mereka biasanya digunakan oleh aplikasi visual yang seperti vi, lynx, mutt, dialogdi mana teks ditulis pada posisi yang sewenang-wenang di layar.

Sekarang, semua emulator terminal X11 dan beberapa yang bukan X11 lainnya seperti GNU screenmemungkinkan Anda memilih area layar untuk menempelkan salinan. Saat Anda memilih bagian dari apa yang Anda lihat di vieditor, Anda tidak ingin menyalin semua urutan pelarian yang telah digunakan untuk menghasilkan output itu. Anda ingin memilih teks yang Anda lihat di sana.

Misalnya jika Anda menjalankan:

printf 'abC\rAC\bB\t\e[C\b\bD\n'

Yang mensimulasikan sesi editor tempat Anda masuk abC, kembali ke awal, ganti abdengan AC, Cdengan B, pindah ke perhentian tab berikutnya, lalu satu kolom lagi ke kanan, lalu dua kolom ke kiri, lalu masuk D.

Kamu melihat:

ABC    D

Itu adalah,, ABCcelah 4 kolom dan D.

Jika Anda memilihnya dengan mouse xtermatau putty, mereka akan menyimpan dalam seleksi ABC, 4 karakter spasi dan D, tidak abC<CR>AC<BS>B<Tab><Esc>[C<BS><BS>D.

Apa yang berakhir dalam pemilihan adalah apa yang telah dikirim oleh printftetapi setelah diproses oleh driver terminal dan terminal emulator.

Untuk jenis transformasi lain, lihat <U+0065><U+0301>( ediikuti oleh aksen akut kombinasi) yang diubah menjadi <U+00E9>( ébentuk yang sudah dibuat sebelumnya) oleh xterm.

Atau echo abcyang akhirnya diterjemahkan ABColeh pengemudi terminal sebelum dikirim ke terminal setelah a stty olcuc.

Sekarang,, <Tab>like <LF>adalah salah satu dari beberapa karakter kontrol yang sebenarnya kadang-kadang ditemukan dalam file teks (juga <CR>dalam file teks MSDOS, dan kadang <FF>- kadang untuk page break).

Jadi beberapa emulator terminal memilih untuk menyalinnya bila mungkin di copy-paste buffer untuk menjaga mereka (yang umumnya tidak kasus <CR>atau <LF>meskipun).

Misalnya, di terminal berbasis VTE seperti gnome-terminal, Anda mungkin melihat bahwa, ketika Anda memilih output printf 'a\tb\n'pada saluran kosong, gnome-terminalsebenarnya menyimpan a\tbdalam pemilihan X11, bukannya a7 spasi dan b.

Tapi untuk output printf 'a\t\bb\n', itu toko a, 6 ruang dan b, dan untuk printf 'a\r\tb\n', a, 7 ruang dan b.

Ada kasus lain di mana terminal akan mencoba untuk menyalin input yang sebenarnya, seperti ketika Anda memilih dua baris setelah berjalan di printf 'a \nb\n'mana ruang trailing yang tidak terlihat itu akan dipertahankan. Atau ketika memilih dua garis tidak termasuk karakter LF ketika dua garis hasil dari pembungkus di margin yang tepat.

Sekarang, jika Anda ingin menyimpan output printfke dalam pilihan CLIPBOARD X11, yang terbaik adalah melakukannya secara langsung seperti dengan:

printf 'foo\tbar\n' | xclip -sel c

Perhatikan bahwa ketika Anda menempelkannya di xtermatau sebagian besar terminal lainnya, xtermsebenarnya menggantikannya \ndengan \rkarena itulah karakter yang xtermdikirim ketika Anda menekan Enter(dan driver terminal dapat menerjemahkannya kembali ke \n).

Stéphane Chazelas
sumber
Ini sangat mendalam, terima kasih. Saya telah mencoba solusi xclip dan berhasil. Tapi itu tidak melakukan persis apa yang ada dalam pikiran saya dan membutuhkan X11. Mungkin ini akan berguna di beberapa titik, terima kasih!
Asu
@ Ain, X11adalah yang menangani pemilihan salin-tempel di emulator terminal seperti xtermatau puttydi Unix. Emulator terminal lain mungkin memiliki mekanisme copy-paste dan cara untuk menyimpan konten sewenang-wenang di sana, seperti perintah readbuf dan registerdi layar GNU.
Stéphane Chazelas