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

?

GAD3R
sumber

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)).

Stéphane Chazelas
sumber
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 &)

zeppelin
sumber
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
11

Menggunakan sed

sed 's/./&\&/g;s/.$//'
αғsнιη
sumber
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
MiniMax
sumber
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 //'
glenn jackman
sumber
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.

Alexander
sumber
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