Jak wstawić spację co cztery znaki w długiej linii?

30

Mam długą linię, w której chcę wstawiać spację co 4 znaki na pojedynczej linii pełnego tekstu, aby ułatwić czytanie, jaki jest najprostszy sposób? także powinienem być w stanie wprowadzić linię z potoku. na przykład

echo "foobarbazblargblurg" | <some command here>

daje

foob arba zbla rgbl urg
ksenoterracid
źródło

Odpowiedzi:

54

Użyj sed w następujący sposób:

$ echo "foobarbazblargblurg" | sed 's/.\{4\}/& /g'
foob arba zbla rgbl urg
dogbane
źródło
1
przeklinanie, które było tak blisko, sedże spróbowałem najpierw, że mogłem się kopnąć.
ksenoterrakid
7
Ciekawe, co się stało z „&”? Och, to skrót od „tego, co właśnie pasowało”. Głupi ja.
Wszechobecny
1
należy zauważyć, że dodaje to spację również na końcu, jeśli w ciągu jest jeszcze jeden znak, co może nie być pożądane
Anubis
@Anubis's/.\{4\}/& /g;s/ $//'
wieczorek1990
21

Możesz użyć następującego prostego przykładu:

$ echo "foobarbazblargblurg" | fold -w4 | paste -sd' ' -
foob arba zbla rgbl
kenorb
źródło
Bardzo miło ... Myślę, że to nawet lepsze niż sedodpowiedź. Nie wiedziałem o tym foldwcześniej.
Wildcard
1
Niestety w obecnych wersjach GNU foldnie działa ze znakami wielobajtowymi (jak echo €€€€€€€€ | fold -w4 | paste -sd' ' -w UTF-8).
Stéphane Chazelas
3

Oto przykład użycia grepi xargs:

$ echo "foobarbazblargblurg" | grep -o .... | xargs
foob arba zbla rgbl
kenorb
źródło
xargsdziała echodomyślnie, więc nie będzie działać ze słowami typu -nenlub zawierającymi ukośniki odwrotne w zależności od echoimplementacji. Od czasu do czasu zobaczysz także dziwny znak nowej linii, jeśli xargs działa na więcej niż jednym echo. paste -sd ' ' -Zamiast tego lepiej jest potokować . Pamiętaj, że -onie jest to standardowa opcja.
Stéphane Chazelas
3

Tylko w bash, brak zewnętrznych poleceń:

str="foobarbazblargblurg"
[[ $str =~ ${str//?/(.)} ]]
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"

lub jako wersja jednorurowa:

echo foobarbazblargblurg | 
  { IFS= read -r str; [[ $str =~ ${str//?/(.)} ]]; \
    printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"; }

Działa to w ten sposób, że konwertuje każdy znak ciągu na „(.)” W celu dopasowania wyrażenia regularnego i przechwytywania za pomocą =~, a następnie po prostu wyprowadza przechwycone wyrażenia z BASH_REMATCH[]tablicy, pogrupowane według potrzeb. Wiodące / końcowe / pośrednie spacje są zachowane, usuń cudzysłowy, "${BASH_REMATCH[@]:1}"aby je pominąć.

Tutaj jest zawinięty w funkcję, ta przetworzy swoje argumenty lub odczyta stdin, jeśli nie ma żadnych argumentów:

function fmt4() {
  while IFS= read -r str; do
    [[ $str =~ ${str//?/(.)} ]]
    printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"
  done < <( (( $# )) && printf '%s\n' "$@" || printf '%s\n' $(< /dev/stdin) )
}

$ echo foobarbazblargblurg | fmt4
foob arba zbla rgbl urg 

Możesz łatwo sparametryzować liczbę, aby odpowiednio dostosować ciąg formatu.

Dodaje się końcowe miejsce, użyj dwóch printfs zamiast jednego, jeśli jest to problem:

printf "%s%s%s%s" "${BASH_REMATCH[@]:1:4}"
(( ${#BASH_REMATCH[@]} > 5 )) && printf " %s%s%s%s" "${BASH_REMATCH[@]:5}"

Pierwszy printfdrukuje (maksymalnie) pierwsze 4 znaki, drugi warunkowo drukuje wszystkie pozostałe (jeśli występują) z wiodącym odstępem, aby oddzielić grupy. Test obejmuje 5 elementów, a nie 4, które uwzględniają element zerowy.

Uwagi:

  • Shell printfS” %cmoże być stosowany zamiast %s, %c(być może) sprawia, że intencją jaśniejsze, ale to nie jest multi-byte bezpieczny charakter. Jeśli twoja wersja bash jest w stanie, powyższe jest bezpieczne dla znaków wielobajtowych.
  • powłoka printfponownie używa łańcucha formatu, dopóki nie zabraknie argumentów, więc po prostu pochłania 4 argumenty na raz i obsługuje końcowe argumenty (więc nie są potrzebne przypadki krawędziowe, w przeciwieństwie do niektórych innych odpowiedzi, które są prawdopodobnie błędne)
  • BASH_REMATCH[0] to cały dopasowany ciąg, więc dane wyjściowe zaczynają się od indeksu 1
  • printf -v myvar ...zamiast tego użyj do zapisania do zmiennej myvar(z zastrzeżeniem zwykłego zachowania w pętli odczytu / podpowłoce)
  • dodaj w printf "\n"razie potrzeby

Możesz włączyć powyższe, zshjeśli match[]zamiast tego użyjesz tablicy BASH_REMATCH[]i odejmiesz 1 od wszystkich indeksów, ponieważ zshnie zachowuje elementu 0 z całym dopasowaniem.

pan. spuratic
źródło
3

Z zshtylko:

str=foobarbazblargblurg

set -o extendedglob
printf '%s\n' ${str//(#m)????/$MATCH }

Lub

printf '%s%s%s%s ' ${(s::)str}

ksh93tylko z :

printf '%s\n' "${str//????/\0 }"

Tylko z dowolną powłoką POSIX (unikając również spacji końcowej, jeśli długość wejściowa jest wielokrotnością 4):

out=
while true; do
  case $str in
    (?????*)
      new_str=${str#????}
      out=$out${str%"$new_str"}' '
      str=$new_str
      ;;
    (*)
      out=$out$str
      break
  esac
done
printf '%s\n' "$out"

To dotyczy postaci . Jeśli chcesz to zrobić w klastrach grafemów (na przykład, aby łamać Stéphane, napisane jako $'Ste\u0301phane', Stép hanei nie Ste phan e), z zsh:

set -o rematchpcre
str=$'Ste\u301phane' out=
while [[ $str =~ '(\X{4})(.+)' ]] {
  out+="$match[1] " str=$match[2]
}
out+=$str
printf '%s\n' $out

Z ksh93 możesz również podzielić według szerokości wyświetlania, co by działało w przypadku Stéphanepowyższego, ale może również pomóc, gdy w grę wchodzą inne rodzaje znaków o zerowej lub podwójnej szerokości:

str=$'Ste\u301phane' out=
while
  start=${ printf %L.4s. "$str"; }
  start=${start%.}
  [ "$start" != "$str" ]
do
  out+="$start " str=${str#"$start"}
done
out+=$str
printf '%s\n' "$out"
Stéphane Chazelas
źródło
2

Odpowiem tylko wstawiając spacje zgodnie z wymaganiami, aby spacja pojawiała się co najmniej po każdych 4 znakach w linii; nie jestem pewien, w jaki sposób chcesz obsłużyć tę skrzynkę. Na przykład, biorąc pod uwagę wejście „aa bbccdd”, otrzymasz wynik „aa bbcc dd” zamiast „aa b bccd d”.

Używam Perla do wyszukiwania z wyprzedzeniem, ale ogólnie nie znam się zbyt dobrze na Perlu, więc mogą być potrzebne poprawki:

$ echo "foobarbazblargblurg" | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g'
foob arba zbla rgbl urg

$ echo 'aa bbccdd' | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g'
aa bbcc dd
# not 'aa b bccd d'!

$ echo 'some input' | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g'
some inpu t
# not 'some  inp ut'!

$ echo $'aabb\nc cddee' | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g' | 
> while read; do echo "${REPLY}x"; done
aabbx
c cdde ex
# no spaces added at the end of the first line (while loop to add to the end of
# the line and show this)
Fred Nurk
źródło
0

Zrobiłem to za pomocą Pythona

Najpierw czytam plik, potem dzielę przez 4 znaki i dodam spację

#!/usr/bin/python
import re
b=re.compile(r'[a-z]{4}')

p=open('/root/l.txt','r')
i=p.readlines()
for j in i:
    m=re.findall(b,j)
print " " .join (m) + "  "

/root/l.txt ==> Składa się z treści podanej w przykładzie

wydajność

foob arba zbla rgbl
Praveen Kumar BS
źródło