Dalam `sed` bagaimana saya bisa menempatkan satu" & "di antara karakter dalam sebuah string?


11

Dapat sedmembuat sesuatu seperti:

12345

menjadi:

1&2&3&4&5

?

Jawaban:


25

Dengan GNU sed:

sed 's/./\&&/2g'

( substitute setiap ( g) karakter ( .) dengan sama ( &) didahului dengan &( \&) tetapi hanya dimulai dari kemunculan kedua ( 2)).

Mudah dibawa:

sed 's/./\&&/g;s/&//'

(ganti setiap kejadian, tetapi kemudian hapus yang pertama &yang tidak kita inginkan).

Dengan beberapa awkimplementasi (bukan POSIX karena perilaku tidak ditentukan untuk FS kosong):

awk -F '' -v OFS="&" '{$1=$1;print}'

(dengan gawkdan beberapa awkimplementasi lainnya , pemisah bidang kosong membagi catatan ke dalam konstituen karakternya . Pemisah bidang keluaran ( OFS) diatur ke &. Kami menetapkan nilai untuk $1(itu sendiri) untuk memaksa catatan untuk dibuat ulang dengan pemisah bidang baru sebelum mencetaknya, NF=NFjuga berfungsi dan sedikit lebih efisien di banyak implementasi awk tetapi perilaku ketika Anda melakukannya saat ini tidak ditentukan oleh POSIX).

perl:

perl -F -lape '$_=join"&",@F' 

( -pemenjalankan kode untuk setiap baris, dan mencetak hasilnya ( $_); -lstrip dan menambahkan kembali akhir baris secara otomatis; -adiisi @Fdengan input split pada pembatas yang ditetapkan -F, yang di sini adalah string kosong. Hasilnya adalah untuk membagi setiap karakter menjadi @F, kemudian gabungkan dengan '&', dan cetak garisnya.)

Kalau tidak:

perl -pe 's/(?<=.)./&$&/g' 

(ganti setiap karakter asalkan diawali oleh karakter lain (lihat-belakang operator regexp (? <= ...))

Menggunakan zshoperator shell:

in=12345
out=${(j:&:)${(s::)in}}

(Sekali lagi, pisah pada pemisah bidang kosong menggunakan s::bendera ekspansi parameter, dan bergabung dengan &)

Atau:

out=${in///&} out=${out#?}

(ganti setiap kemunculan yang tidak ada (jadi sebelum setiap karakter) dengan &menggunakan ${var//pattern/replacement}operator ksh (meskipun dalam kshpola kosong berarti sesuatu yang lain, namun sesuatu yang lain, saya tidak yakin apa yang ada di dalamnya bash), dan lepaskan yang pertama dengan ${var#pattern}stripping POSIX operator).

Menggunakan ksh93operator shell:

in=12345
out=${in//~(P:.(?=.))/\0&}

( ~(P:perl-like-RE)menjadi operator ksh93 glob untuk menggunakan ekspresi reguler seperti perl (berbeda dari perl atau PCRE), (?=.)menjadi operator yang melihat ke depan: ganti karakter asalkan diikuti oleh karakter lain dengan sendirinya ( \0) dan &)

Atau:

out=${in//?/&\0}; out=${out#?}

(ganti setiap karakter ( ?) dengan &dan dirinya sendiri ( \0), dan kami menghapus yang superflous)

Menggunakan bashoperator shell:

shopt -s extglob
in=12345
out=${in//@()/&}; out=${out#?}

(sama seperti zsh's, kecuali bahwa Anda perlu @()ada (operator ksh gumpal yang Anda butuhkan extglobdalam bash)).


2
@AFSHIN, itu tidak akan berfungsi pada 012345input
Stéphane Chazelas

1
ini seharusnya bekerjaawk -F '' -v OFS="&" 'NF=NF'
αғsнιη

1
@ AFSHIN, tetapi hapus baris kosong. Secara lebih umum, ketika menggunakan suatu tindakan sebagai kondisi dan bermaksud hasil dari tindakan yang akan dicetak, Anda perlu memastikan nilai yang dikembalikan oleh tindakan tersebut bukan string kosong atau string numerik yang dipecahkan menjadi 0.
Stéphane Chazelas

1
Bisakah Anda menambahkan penjelasan singkat tentang bagaimana masing-masing bekerja? Sepertinya ada beberapa hal yang luar biasa untuk dipelajari di sini, tetapi saya bahkan tidak tahu di mana saya akan mulai meneliti sebagian besar dari mereka untuk melihat bagaimana menerapkannya di luar lingkup masalah khusus ini.
IMSoP

1
@ StéphaneChazelas Brilliant, terima kasih. Mencari dokumen kompleks untuk hal-hal seperti sed adalah sedikit seni, jadi memiliki beberapa contoh adalah cara yang bagus untuk mempelajari bit baru yang belum pernah Anda lihat sebelumnya.
IMSoP

15

Utilitas Unix:

fold -w1|paste -sd\& -

Dijelaskan:

"fold -w1" - akan membungkus setiap karakter input ke barisnya sendiri

lipat - bungkus setiap jalur input agar sesuai dengan lebar yang ditentukan

-w, --width = WIDTH gunakan kolom WIDTH dan bukan 80

%echo 12345|fold -w1
1
2
3
4
5

"paste -sd\& -"- akan menggabungkan garis input bersama, menggunakan &sebagai pemisah

tempel - gabungkan baris file

-s, --serial paste satu file sekaligus bukan secara paralel

-d, --delimiters = LIST menggunakan kembali karakter dari LIST alih-alih TAB

%fold -w1|paste -sd\& -
1&2&3&4&5

(Perhatikan bahwa jika input berisi beberapa baris, mereka akan bergabung dengan &)


2
Gagal pada karakter multibyte. Cobaecho "abcdeéèfg" | fold -1 | paste -sd\& -
Isaac

3
@Arrow Kemungkinan besar Anda hanya menggunakan versi flip coreut buggy , yang tidak memiliki dukungan Unicode penuh. BSD fold, versi RedHat-patched dari coreutils (yaitu Fedora atau CentOS) serta implementasi BusyBox, dapat menangani Unicode dengan baik.
zeppelin

5
Pertanyaannya secara khusus tentang sed.
Alexander

6
@Alexander - itu benar, dan ada sejumlah sedjawaban bagus di bawah ini. Dan saya tidak melihat ada salahnya menunjukkan bagaimana tugas itu dapat diselesaikan dengan cara lain.
zeppelin

@ StéphaneChazelas> POSIXly, Anda perlu flip -w 1 Benar, saya sudah menambahkan "-w", thx! "-", pada gilirannya, tidak diperlukan If no file operands are specified, the standard input shall be used
zeppelin


9
sed 's/\B/\&/g'

\ B - Cocok di mana saja tetapi pada batas kata; itu cocok jika karakter di sebelah kiri dan karakter di sebelah kanan adalah karakter "kata" atau "non-kata".

Informasi: Manual sed GNU, ekstensi ekspresi reguler .

Pengujian:

sed 's/\B/\&/g' <<< '12345'
1&2&3&4&5

5
Ide yang menarik tetapi pertanyaannya tidak mengatakan bahwa string tidak mengandung spasi, titik atau apa pun yang dapat membentuk batas kata. Itu hanya mengatakan "antara karakter" yang harus ditafsirkan sebagai "karakter apa saja".
xhienne

4

Ini akan sedikit lebih lambat dari beberapa jawaban lain, tetapi cukup jelas:

echo 12345 | perl -lnE 'say join "&", split //'

4

Ini cara lain. Bagian pertama dari ekspresi sed menangkap setiap karakter kemudian menggantikannya dengan karakter dan ampersand. Bagian kedua menghapus ampersand dari ujung garis.

echo 12345 | sed -r 's/(.)/\1\&/g;s/\&$//g'
1&2&3&4&5

Bekerja pada karakter multibyte juga.


1
Tidak perlu menelepon seddua kali, sebuah sedskrip mungkin memiliki beberapa perintah:sed -r 's/(.)/\1\&/g; s/\&$//g'
xhienne

xhienne, terima kasih, TIL! Diperbarui jawabannya.
Alexander
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.