W `sed` jak mogę umieścić jedno„ & ”między znakami w ciągu?

11

Może sedzrobić coś takiego:

12345

stają się :

1&2&3&4&5

?

GAD3R
źródło

Odpowiedzi:

25

Z GNU sed:

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

( sUbstitute każdy ( g) znak ( .) z tym samym ( &) poprzedzone &( \&), ale dopiero od drugiego wystąpienia ( 2)).

Przenośny:

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

(zamień każde wystąpienie, ale następnie usuń pierwsze, &którego nie chcemy).

W przypadku niektórych awkimplementacji (nie POSIX, ponieważ zachowanie nie jest określone dla pustego FS):

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

(z gawkkilkoma innymi awkimplementacjami, pusty separator pól dzieli rekordy na składniki znakowe . Separator pól wyjściowych ( OFS) jest ustawiony na &. Przypisujemy wartość do $1(samej), aby wymusić odtworzenie rekordu za pomocą nowego separatora pól przed wydrukowaniem NF=NFdziała również i jest nieco bardziej wydajny w wielu implementacjach awk, ale zachowanie, kiedy to robisz, nie jest obecnie określone przez POSIX).

perl:

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

( -peuruchamia kod dla każdego wiersza i wypisuje wynik ( $_); automatycznie -lusuwa paski i ponownie dodaje zakończenia wierszy; -awypełnia @Fsię wprowadzonym podziałem wejściowym w ustawionym ograniczniku -F, który tutaj jest pustym ciągiem. Wynikiem jest podzielenie każdego znaku na @F, następnie połącz je za pomocą „&” i wydrukuj wiersz).

Alternatywnie:

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

(zamień każdy znak, pod warunkiem, że poprzedza go inny znak (patrz operator wyrażenia regularnego (? <= ...))

Korzystanie z zshoperatorów powłoki:

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

(ponownie podziel na pusty separator pól przy użyciu s::flagi rozwijania parametrów i połącz się z &)

Lub:

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

(zastąpić każde wystąpienie nic (tak przed każdym znaku) z &użyciem ${var//pattern/replacement}operatora ksh (choć w kshpustym środki wzór czegoś innego, a jeszcze coś innego, nie jestem pewien, co w bash) i wyjąć pierwszy z POSIX ${var#pattern}stripping operator).

Korzystanie z ksh93operatorów powłoki:

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

( ~(P:perl-like-RE)będąc operatorem globalnym ksh93, który używa wyrażeń regularnych podobnych do perla (różniących się od perla lub PCRE), (?=.)będąc operatorem wybiegającym w przyszłość: zamień znak pod warunkiem, że następuje po nim inny znak ze sobą ( \0) i &)

Lub:

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

(zamień każdy znak ( ?) na &i sama ( \0), a my usuniemy zbędny)

Korzystanie z bashoperatorów powłoki:

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

(to samo co zsh„s, z wyjątkiem, że trzeba @()tam (operator ksh glob, dla którego trzeba extglobw bash)).

Stéphane Chazelas
źródło
2
@AFSHIN, to nie działałoby na 012345danych wejściowych
Stéphane Chazelas
1
to powinno zadziałaćawk -F '' -v OFS="&" 'NF=NF'
αғsнιη
1
@AFSHIN, ale usuń puste linie. Mówiąc bardziej ogólnie, gdy używasz akcji jako warunku i zamierzasz wydrukować wynik akcji, musisz upewnić się, że wartość zwracana przez akcję nie jest pustym ciągiem znaków lub ciągiem liczbowym, który
zamienia się
1
Czy możesz dodać krótkie wyjaśnienie działania każdego z nich? Wygląda na to, że jest tu kilka niesamowitych rzeczy do nauczenia się, ale nawet nie wiem, gdzie zacznę badać większość z nich, aby zobaczyć, jak zastosować je poza zakresem tego konkretnego problemu.
IMSoP,
1
@ StéphaneChazelas Genialne, dzięki. Wyszukiwanie skomplikowanych dokumentów w celu znalezienia czegoś takiego jak sed jest sztuką, więc posiadanie praktycznych przykładów to świetny sposób na naukę nowych fragmentów, których wcześniej nie widziałeś.
IMSoP
15

Narzędzia uniksowe:

fold -w1|paste -sd\& -

Wyjaśnione:

"fold -w1" - zawinie każdy znak wejściowy do własnej linii

fold - zawiń każdą linię wejściową, aby dopasować ją do określonej szerokości

-w, --width = WIDTH używa kolumn WIDTH zamiast 80

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

"paste -sd\& -"- scali linie wejściowe razem, używając &jako separatora

wklej - scal linie wierszy plików

-s, --serial wklejaj jeden plik na raz zamiast równolegle

-d, --delimiters = LISTA ponownie wykorzystuje znaki z LISTY zamiast TAB

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

(Uwaga: jeśli dane wejściowe zawierają kilka wierszy, zostaną połączone &)

zepelin
źródło
2
Błąd na znakach wielobajtowych. Spróbujecho "abcdeéèfg" | fold -1 | paste -sd\& -
Izaak,
3
@Arrow Najprawdopodobniej jesteś po prostu za pomocą buggy Coreutils wersję krotnie , który nie posiada pełną obsługę Unicode. Składanie BSD, łatane przez RedHat wersje coreutils (tj. Fedora lub CentOS), a także implementacja BusyBox, mogą po prostu ładnie obsługiwać Unicode.
zeppelin
5
Pytanie dotyczy konkretnie sed.
Alexander
6
@Alexander - to prawda, a sedponiżej znajduje się wiele dobrych odpowiedzi. I nie widzę żadnej szkody w demonstrowaniu, jak zadanie można rozwiązać innymi sposobami.
zeppelin
@ StéphaneChazelas> POSIXly, potrzebujesz fold -w 1 To prawda, dodałem "-w", dzięki! "-"z kolei nie jest wymagane If no file operands are specified, the standard input shall be used
zeppelin
11

Posługiwać się sed

sed 's/./&\&/g;s/.$//'
αғsнιη
źródło
9
sed 's/\B/\&/g'

\ B - Pasuje wszędzie, ale na granicy słów; to znaczy pasuje, jeśli znak po lewej stronie i znak po prawej stronie są zarówno znakami „słownymi”, jak i obydwoma znakami „niebędącymi słowami”.

Informacja: Podręcznik GNU sed, rozszerzenia wyrażeń regularnych .

Testowanie:

sed 's/\B/\&/g' <<< '12345'
1&2&3&4&5
MiniMax
źródło
5
Ciekawy pomysł, ale pytanie nie mówi, że ciąg nie zawiera spacji, kropki ani niczego, co mogłoby stanowić granicę słowa. Mówi tylko „między znakami”, co należy interpretować jako „dowolne znaki”.
xhienne,
4

To będzie trochę wolniejsze niż niektóre inne odpowiedzi, ale jest całkiem jasne:

echo 12345 | perl -lnE 'say join "&", split //'
Glenn Jackman
źródło
4

Oto inny sposób. Pierwsza część wyrażenia sed przechwytuje każdą postać, a następnie zastępuje ją znakiem i znakiem ampersand. Druga część usuwa znaki handlowe i koniec linii.

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

Działa również na znakach wielobajtowych.

Alexander
źródło
1
Nie trzeba dzwonić seddwa razy, sedskrypt może mieć kilka poleceń:sed -r 's/(.)/\1\&/g; s/\&$//g'
xhienne
Xhienne, dzięki, TIL! Zaktualizowałem odpowiedź.
Alexander