강제 sed해결

강제 sed해결

더 많은 행에 대해 내 코드를 작동시키는 방법을 모르겠습니다.

원본 파일 t.txt는 다음과 같습니다.

Hello Earth
Hello Mars

그러나 나는 다음과 같은 결과를 얻습니다.

Mars Hello Earth Hello

내 예상 결과는 다음과 같습니다.

Earth Hello
Mars Hello

일반적으로 줄 순서는 동일하게 유지하고 싶지만 단어가 반대입니다. 일반적인 경우 입력은 다음과 같습니다.

one two 
four five

예상되는 출력은 다음과 같습니다.

two one
five four

내 코드는 다음과 같습니다.

#!/bin/bash
text=$(cat $1)
arr=($text)
al=${#arr[@]}
let al="al-1"

while (($al >= 0))
do
    echo -n "${arr[al]}"
    echo -n " "
    let al="al - 1"
done

echo

답변1

아래 제공된 모든 예는 한 줄에 단어 수에 관계없이 일반적인 경우에 적용됩니다. 기본 아이디어는 어디에서나 동일합니다. 파일을 한 줄씩 읽고 단어를 반대로 인쇄해야 합니다. AWK는 프로그래밍 방식으로 텍스트 처리를 수행하는 데 필요한 모든 도구를 이미 갖추고 있고 가장 이식성이 높기 때문에 이를 가장 잘 촉진합니다. 모든 awk 파생물과 함께 사용할 수 있으며 대부분의 시스템에 있습니다. Python에는 작업을 완료하는 데 도움이 되는 훌륭한 문자열 조작 유틸리티도 많이 있습니다. 나는 이것이 보다 현대적인 시스템을 위한 도구라고 말하고 싶습니다. IMHO, Bash는 이식성, 잠재적인 위험 및 수행해야 하는 "속임수" 작업의 양으로 인해 가장 이상적인 접근 방식입니다.

AWK

$ awk '{for(i=NF;i>=1;i--) printf "%s ", $i;print ""}' input.txt                                                  
Earth Hello 
Mars Hello 

작동 방식은 매우 간단합니다. 줄의 각 단어를 뒤로 반복하여 공백으로 구분된 단어를 인쇄합니다. 이는 printf "%s ",$i형식화된 문자열을 인쇄하는 함수와 for 루프를 사용하여 수행됩니다. NF변수는 필드 수에 해당합니다. 기본 필드 구분 기호는 공백으로 간주됩니다. 먼저 일회성 변수를 i단어 수로 설정한 다음 각 반복마다 해당 변수를 감소시킵니다. 따라서 한 줄에 3개의 단어가 있으면 $3 필드, $2 및 $1 필드를 인쇄합니다. 마지막 패스 후에 변수 i는 0이 되고 조건은 i>=1false가 되며 루프가 종료됩니다. 줄이 서로 이어지는 것을 방지하기 위해 삽입된 개행 문자를 사용합니다 print "". 이 경우 AWK 코드 블록은 {}각 라인마다 처리됩니다(코드 블록 이전에 일치 조건이 있는 경우 일치 여부에 따라 다름).

파이썬

대체 솔루션을 선호하는 사람들을 위해 Python을 사용합니다.

$ python -c "import sys;print '\n'.join([ ' '.join(line.split()[::-1]) for line in sys.stdin ])"  < input.txt     
Earth Hello
Mars Hello

여기서의 생각은 약간 다릅니다. <연산자는 현재 쉘에 input.txtPython의 stdin스트림으로 리디렉션하도록 지시하고 해당 내용을 한 줄씩 읽습니다. 여기서는 목록 이해를 사용하여 행 목록을 만듭니다. 이것이 바로 이 [ ' '.join(line.split()[::-1]) for line in sys.stdin ]부분의 목적입니다. 이 부분은 ' '.join(line.split()[::-1])한 줄을 가져와 목록을 뒤집어 단어 목록으로 분할한 [::-1]다음 ' '.join()공백으로 구분된 문자열을 만듭니다. 결과적으로 우리는 더 큰 문자열 목록을 얻습니다. 마지막으로 '\n'.join()각 항목이 개행 문자로 연결된 더 큰 문자열이 생성됩니다.

간단히 말해서 이 접근 방식은 기본적으로 "파괴 및 재구축" 접근 방식입니다.

세게 때리다

#!/bin/bash

while IFS= read -r line
do
     bash -c 'i=$#; while [ $i -gt 0 ];do printf "%s " ${!i}; i=$(($i-1)); done' sh $line   
     echo 
done < input.txt

그리고 테스트 실행을 하려면:

$ ./reverse_words.sh                                                                                              
Earth Hello 
Mars Hello 

Bash 자체에는 강력한 텍스트 처리 기능이 없습니다. 여기서 일어나는 일은 파일을 한 줄씩 읽는 것입니다.

while IFS= read -r line
do
   # some code
done < text.txt

이는 명령 또는 텍스트 파일의 출력을 한 줄씩 읽기 위해 쉘 스크립팅에서 널리 사용되는 일반적인 기술입니다. 각 행은 $line변수 에 저장됩니다 .

안에 우리가 있어요

bash -c 'i=$#; while [ $i -gt 0 ];do printf "%s " ${!i}; i=$(($i-1)); done' sh $line

여기서는 작은따옴표로 묶인 명령 집합을 실행하기 위해 bashwith 플래그를 사용합니다. 사용되면 로 시작하는 변수 에 명령줄 인수를 할당하기 시작합니다 . 전통적으로 프로그램 이름을 나타내는 데 사용되기 때문에 먼저 더미 변수를 사용합니다.-c-cbash$0$0sh

인용되지 않은 콘텐츠는 $line토큰화라는 동작으로 인해 별도의 항목으로 구분됩니다. 쉘 스크립팅에서는 일반적으로 토큰화가 바람직하지 않으며 "$foo"와 같은 변수를 항상 인용한다는 말을 자주 듣게 됩니다. 그러나 이 경우 토큰화는 간단한 텍스트를 처리하는 데 유용하다고 합니다. 텍스트에 이와 같은 내용이 포함되어 있으면 $var이 접근 방식이 깨질 수 있습니다. 이런 이유와 다른 여러 가지 이유로 저는 Python과 awk 접근 방식이 더 좋다고 생각합니다.

내부 코드에 관해서도 간단합니다. 인용되지 않은 부분을 $line단어로 분할하고 처리를 위해 내부 코드에 전달합니다. 우리는 인수의 수를 얻고 $#, 그것을 던져진 변수에 저장하고 i, 변수 간접 참조라는 것을 사용하여 각 항목을 다시 인쇄합니다. 이것이 바로 그 부분입니다 ${!i} (이것은 bashism입니다. 다른 쉘에서는 사용할 수 없습니다). 이번에도 printf "%s "각 단어를 공백으로 구분하여 인쇄합니다. 완료되면 echo줄 바꿈이 추가됩니다.

기본적으로 이 접근 방식은 awk와 Python을 혼합한 것입니다. 파일을 한 줄씩 읽지만 bash작업을 수행하기 위해 이러한 여러 기능을 사용하여 각 줄을 나누고 정복합니다.

tacGNU 명령을 사용 하고 다시 단어 분리기를 사용하여 더 간단한 변형을 수행할 수 있습니다 . tac입력 스트림이나 파일의 줄을 바꾸는 데 사용되지만 이 경우 -s " "공백을 구분 기호로 사용하도록 지정합니다. 따라서 var줄 바꿈으로 구분된 단어 목록이 역순으로 포함되지만, $var인용되지 않으므로 줄 바꿈은 공백으로 대체됩니다. 트릭은 다시 가장 신뢰할 수는 없지만 효과적입니다.

#!/bin/bash

while IFS= read -r line
do
     var=$(tac -s " " <<< "$line" )
     echo  $var
done < input.txt

테스트 실행:

임의의 입력 행을 갖는 3가지 방법은 다음과 같습니다.

$ cat input.txt                                                                                                   
Hello Earth end of line
Hello Mars  another end of line
abra cadabra magic
$ ./reverse_words.sh                                                                                              
line of end Earth Hello 
line of end another Mars Hello 
magic cadabra abra 
$ python -c "import sys;print '\n'.join([ ' '.join(line.split()[::-1]) for line in sys.stdin ])"  < input.txt  
line of end Earth Hello
line of end another Mars Hello
magic cadabra abra
$ awk '{for(i=NF;i>=1;i--) printf "%s ", $i;print ""}' input.txt
line of end Earth Hello 
line of end another Mars Hello 
magic cadabra abra 

추가사항: 펄과 루비

Python과 같은 아이디어입니다. 각 줄을 단어 배열로 나누고 배열을 뒤집어서 인쇄합니다.

$ perl -lane '@r=reverse(@F); print "@r"' input.txt                           
line of end Earth Hello
line of end another Mars Hello
magic cadabra abra


$ ruby -ne 'puts $_.chomp.split().reverse.join(" ")' < input.txt                                                  
line of end Earth Hello
line of end another Mars Hello
magic cadabra abra

답변2

단어를 바꾸세요 awk.

awk '{print $2, $1}'

예:

% cat bar.txt
Hello Earth
Hello Mars

% awk '{print $2, $1}' bar.txt
Earth Hello
Mars Hello

답변3

강제 sed해결

다음 GNU sed프로그램은 루프를 사용하여 각 단어를 줄 끝(첫 번째 단어부터 시작)으로 이동합니다. 자세한 내용은 코드에 주석으로 삽입됩니다.

sed -r '
    # Mark the current end of the line by appending a LF character ("\n")
    G

    # Main loop: move the first word of the line just after the LF
    # and repeat until the LF is at the beginning of the line
    :loop
    s/([^[:space:]]+)(.*\n)/\2\1 /
    t loop

    # Remove remaining spaces up to the LF and the superfluous trailing space
    s/.*\n| $//g
'

쓰기 전용 버전:

sed -r 'G; :loop; s/(\S+)(.*\n)/\2\1 /; t loop; s/.*\n| $//g'

시험:

$ sed -r '...' <<< "The quick
brown fox jumps

over
the lazy dog"

...생산하다:

quick The 
jumps fox brown 

over 
dog lazy the 

휴대용(POSIXly):

sed '
  G
  :loop
     s/\([^[:space:]]\{1,\}\)\(.*\n\)/\2\1 /
  t loop
  s/ $//
  s/.*\n//'

답변4

rev문자와 줄은 있지만 tac(내가 알 수 있는 한) 텍스트는 없습니다. 이것은 나에게 가장 간단한 Bash 관용구입니다.

while read line; do echo $(echo $line | tr " " "\n" | tac); done < $1

관련 정보