![강제 sed해결](https://linux55.com/image/102773/%EA%B0%95%EC%A0%9C%20sed%ED%95%B4%EA%B2%B0.png)
더 많은 행에 대해 내 코드를 작동시키는 방법을 모르겠습니다.
원본 파일 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>=1
false가 되며 루프가 종료됩니다. 줄이 서로 이어지는 것을 방지하기 위해 삽입된 개행 문자를 사용합니다 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.txt
Python의 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
여기서는 작은따옴표로 묶인 명령 집합을 실행하기 위해 bash
with 플래그를 사용합니다. 사용되면 로 시작하는 변수 에 명령줄 인수를 할당하기 시작합니다 . 전통적으로 프로그램 이름을 나타내는 데 사용되기 때문에 먼저 더미 변수를 사용합니다.-c
-c
bash
$0
$0
sh
인용되지 않은 콘텐츠는 $line
토큰화라는 동작으로 인해 별도의 항목으로 구분됩니다. 쉘 스크립팅에서는 일반적으로 토큰화가 바람직하지 않으며 "$foo"와 같은 변수를 항상 인용한다는 말을 자주 듣게 됩니다. 그러나 이 경우 토큰화는 간단한 텍스트를 처리하는 데 유용하다고 합니다. 텍스트에 이와 같은 내용이 포함되어 있으면 $var
이 접근 방식이 깨질 수 있습니다. 이런 이유와 다른 여러 가지 이유로 저는 Python과 awk 접근 방식이 더 좋다고 생각합니다.
내부 코드에 관해서도 간단합니다. 인용되지 않은 부분을 $line
단어로 분할하고 처리를 위해 내부 코드에 전달합니다. 우리는 인수의 수를 얻고 $#
, 그것을 던져진 변수에 저장하고 i
, 변수 간접 참조라는 것을 사용하여 각 항목을 다시 인쇄합니다. 이것이 바로 그 부분입니다 ${!i}
(이것은 bashism입니다. 다른 쉘에서는 사용할 수 없습니다). 이번에도 printf "%s "
각 단어를 공백으로 구분하여 인쇄합니다. 완료되면 echo
줄 바꿈이 추가됩니다.
기본적으로 이 접근 방식은 awk와 Python을 혼합한 것입니다. 파일을 한 줄씩 읽지만 bash
작업을 수행하기 위해 이러한 여러 기능을 사용하여 각 줄을 나누고 정복합니다.
tac
GNU 명령을 사용 하고 다시 단어 분리기를 사용하여 더 간단한 변형을 수행할 수 있습니다 . 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