sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
Saya mengharapkan sed
skrip ini untuk menyisipkan tab
di depan setiap baris $filename
tetapi sebenarnya tidak. Untuk beberapa alasan itu memasukkan sebagai t
gantinya.
sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
Saya mengharapkan sed
skrip ini untuk menyisipkan tab
di depan setiap baris $filename
tetapi sebenarnya tidak. Untuk beberapa alasan itu memasukkan sebagai t
gantinya.
Jawaban:
Tidak semua versi sed
mengerti \t
. Cukup masukkan tab literal sebagai gantinya (tekan Ctrl- Vlalu Tab).
\t
di bagian pengganti ekspresi (itu dikenali \t
di bagian pencocokan pola dengan baik)
Menggunakan Bash Anda dapat memasukkan karakter TAB secara terprogram seperti ini:
TAB=$'\t'
echo 'line' | sed "s/.*/${TAB}&/g"
echo 'line' | sed 's/.*/'"${TAB}"'&/g' # use of Bash string concatenation
$'string'
penjelasan yang kurang. Sebenarnya saya curiga, karena penggunaan yang sangat canggung sehingga Anda mungkin memiliki pemahaman yang tidak lengkap (seperti yang kebanyakan kita lakukan dengan bash). Lihat penjelasan saya di bawah ini: stackoverflow.com/a/43190120/117471
$TAB
di dalam tanda kutip tunggal, jadi Anda harus menggunakannya tanda kutip ganda.
*
tanda kutip ganda di dalam ... ini akan diperlakukan sebagai bola, bukan sebagai ekspresi reguler yang Anda inginkan.
@sedit berada di jalur yang benar, tetapi agak canggung untuk mendefinisikan variabel.
Cara untuk melakukan ini di bash adalah dengan meletakkan tanda dolar di depan string kutipan tunggal Anda.
$ echo -e '1\n2\n3'
1
2
3
$ echo -e '1\n2\n3' | sed 's/.*/\t&/g'
t1
t2
t3
$ echo -e '1\n2\n3' | sed $'s/.*/\t&/g'
1
2
3
Jika string Anda perlu menyertakan ekspansi variabel, Anda dapat menggabungkan string yang dikutip seperti ini:
$ timestamp=$(date +%s)
$ echo -e '1\n2\n3' | sed "s/.*/$timestamp"$'\t&/g'
1491237958 1
1491237958 2
1491237958 3
Dalam bash $'string'
menyebabkan "ekspansi ANSI-C". Dan itulah yang sebagian besar dari kita harapkan ketika kita menggunakan hal-hal seperti \t
, \r
, \n
, dll Dari: https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting
Kata-kata dalam bentuk $ 'string' diperlakukan secara khusus. Kata tersebut diperluas menjadi string , dengan karakter pelolosan garis miring terbalik diganti seperti yang ditentukan oleh standar ANSI C. Urutan escape dengan garis miring terbalik, jika ada, didekodekan ...
Hasil yang diperluas adalah kutipan tunggal, seolah-olah tanda dolar tidak ada.
Menurut saya pribadi, sebagian besar upaya untuk menghindari bash itu konyol karena menghindari bashisme TIDAK * membuat kode Anda portabel. (Kode Anda akan kurang rapuh jika Anda shebang ke bash -eu
daripada jika Anda mencoba menghindari bash dan penggunaan sh
[kecuali Anda adalah ninja POSIX mutlak].) Tetapi daripada memiliki argumen religius tentang itu, saya hanya akan memberikan yang TERBAIK * jawaban.
$ echo -e '1\n2\n3' | sed "s/.*/$(printf '\t')&/g"
1
2
3
* Jawaban Terbaik? Ya, karena salah satu contoh kesalahan kebanyakan skrip shell anti-bash dalam kodenya adalah penggunaan echo '\t'
seperti dalam jawaban @ robrecord . Itu akan bekerja untuk GNU echo, tapi tidak BSD echo. Itu dijelaskan oleh The Open Group di http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16 Dan ini adalah contoh mengapa mencoba menghindari bashism biasanya gagal.
Saya telah menggunakan sesuatu seperti ini dengan shell Bash di Ubuntu 12.04 (LTS):
Untuk menambahkan baris baru dengan tab, kedua saat pertama cocok:
sed -i '/first/a \\t second' filename
Untuk mengganti pertama dengan tab, kedua :
sed -i 's/first/\\t second/g' filename
\\t
dan tidak \t
.
Gunakan $(echo '\t')
. Anda membutuhkan tanda kutip di sekitar pola.
Misalnya. Untuk menghapus tab:
sed "s/$(echo '\t')//"
echo '\t'
akan mengeluarkan 2 karakter terpisah. Cara portabel POSIX adalah dengan menggunakan printf '\t'
. Inilah sebabnya saya katakan: Jangan mencoba membuat kode Anda portabel dengan tidak menggunakan bash. Ini lebih sulit dari yang Anda pikirkan. Menggunakan bash
adalah hal paling portabel yang dapat dilakukan kebanyakan dari kita.
Anda tidak perlu menggunakan sed
untuk melakukan substitusi padahal sebenarnya Anda hanya ingin menyisipkan tab di depan baris. Penggantian untuk kasus ini adalah operasi yang mahal dibandingkan dengan hanya mencetaknya, terutama saat Anda bekerja dengan file besar. Lebih mudah dibaca juga karena bukan regex.
misalnya menggunakan awk
awk '{print "\t"$0}' $filename > temp && mv temp $filename
Saya menggunakan ini di Mac:
sed -i '' $'$i\\\n\\\thello\n' filename
sed
tidak mendukung \t
, atau urutan pelarian lainnya seperti \n
dalam hal ini. Satu-satunya cara yang saya temukan untuk melakukannya adalah dengan benar-benar memasukkan karakter tab dalam skrip menggunakan sed
.
Meskipun demikian, Anda mungkin ingin mempertimbangkan untuk menggunakan Perl atau Python. Berikut skrip Python singkat yang saya tulis yang saya gunakan untuk semua ekspresi reguler aliran:
#!/usr/bin/env python
import sys
import re
def main(args):
if len(args) < 2:
print >> sys.stderr, 'Usage: <search-pattern> <replace-expr>'
raise SystemExit
p = re.compile(args[0], re.MULTILINE | re.DOTALL)
s = sys.stdin.read()
print p.sub(args[1], s),
if __name__ == '__main__':
main(sys.argv[1:])
Alih-alih BSD sed, saya menggunakan perl:
ct@MBA45:~$ python -c "print('\t\t\thi')" |perl -0777pe "s/\t/ /g"
hi
Saya pikir orang lain telah menjelaskan ini cukup untuk pendekatan lain ( sed
, AWK
, dll). Namun, bash
jawaban spesifik saya (diuji pada macOS High Sierra dan CentOS 6/7) mengikuti.
1) Jika OP ingin menggunakan metode cari-dan-ganti yang mirip dengan yang mereka usulkan semula, maka saya akan menyarankan penggunaan perl
untuk ini, sebagai berikut. Catatan: garis miring terbalik sebelum tanda kurung untuk regex seharusnya tidak diperlukan, dan baris kode ini mencerminkan cara $1
yang lebih baik untuk digunakan daripada \1
dengan perl
operator substitusi (misalnya per dokumentasi Perl 5 ).
perl -pe 's/(.*)/\t$1/' $filename > $sedTmpFile && mv $sedTmpFile $filename
2) Namun, seperti yang ditunjukkan oleh ghostdog74 , karena operasi yang diinginkan sebenarnya hanya menambahkan tab di awal setiap baris sebelum mengubah file tmp ke file input / target ( $filename
), saya akan merekomendasikan perl
lagi tetapi dengan modifikasi berikut (s):
perl -pe 's/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
## OR
perl -pe $'s/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
3) Tentu saja, file tmp tidak berguna , jadi lebih baik melakukan semuanya 'di tempat' (menambahkan -i
tanda) dan menyederhanakan semuanya menjadi satu baris yang lebih elegan dengan
perl -i -pe $'s/^/\t/' $filename