문자열 목록이 주어지면:
a
b
c
d
e
사용자 입력: 행 4를 행 2로 이동합니다.
따라서 출력은 다음과 같습니다.
a
d
b
c
e
이것이 어떻게 달성될 수 있습니까? sed
'shold 및 패턴 공간 명령을 사용하는 것이 더 좋습니다 .
답변1
1행 이후 4행을 뒤로 이동하는 것은 2, 3행을 이동하는 것과 같습니다.앞으로4번째 줄 이후.
$ sed -e '2 { h; d; }' -e '3 { H; d; }' -e '4 G' file
a
d
b
c
e
즉, 행 2를 예약된 공간에 저장합니다. 그런 다음 다른 모든 행 H
(즉, 행 3만) 을 위한 공간을 예약하기 위해 추가 항목을 이동해야 합니다 . 범위를 이동하려는 행에 예약된 space 를 추가합니다 G
.
5행을 2행 뒤로 이동하려면(즉, 2, 3, 4행을 5행 뒤로 이동) 3,4
두 번째 표현식으로 사용된 범위를 사용하고 마지막 표현식의 범위를 5
대체합니다 .4
일반적으로 한 줄을 a
다음 줄로 이동하는 것 b
(파일의 시작 부분을 향해 이동하는 경우)은 한 줄을 다음 줄로 b+1
이동 하는 것으로 표현할 수 있습니다 .a-1
a
단순히 일련의 줄을 로 이동하는 것으로 표현하면 x
해당 y
줄로 앞으로 이동한 후 다음과 같이 일련의 줄을 앞으로 이동하는 명령을 z
작성할 수 있습니다 .sed
sed -e "$x { h; d; }" -e "$((x+1)),$y { H; d; }" -e "$z G" file
예를 들어, "행 4 뒤에 행 2와 3을 이동하십시오"(질문에서 묻는 내용입니다):
$ x=2 y=3 z=4; sed -e "$x { h; d; }" -e "$((x+1)),$y { H; d; }" -e "$z G" file
a
d
b
c
e
예를 들어 "첫 번째 행과 두 번째 행을 끝으로 이동합니다."
$ x=1 y=2 z='$'; sed -e "$x { h; d; }" -e "$((x+1)),$y { H; d; }" -e "$z G" file
c
d
e
a
b
이것을 사용하여 이동할 수는 없습니다.하나의두 번째 표현식의 범위가 반전되어 잘못된 출력이 생성되므로 앞으로 진행하세요. 단일 행 범위의 경우 두 번째 표현식을 무시해야 합니다. 그렇지 않으면 $x,$y { H; $x h; d; }
첫 번째와 두 번째 표현식을 H
이동했지만(불필요한 한 줄 테스트와 불필요한 줄 번호 테스트를 수행하지만 이후 스크립트에서는 표현식과 논리를 단순화함) 대체할 수 있습니다.
쉘 스크립트에서 두 개의 줄 번호 a
(이동하려는 줄)와 b
뒤에 줄을 삽입하려는 줄이 주어지면 a
다음과 같은 작업을 수행할 수 있습니다.
if [ "$a" -eq "$b" ] || [ "$a" -eq "$((b+1))" ]; then
# No move
set -- -e b
else
if [ "$a" -lt "$b" ]; then
# Move forwards
x="$a" y="$a" z="$b"
else
# Move backwards
x="$((b+1))" y="$((a-1))" z="$a"
fi
set -- -e "$x,$y { H; $x h; d; }" -e "$z G"
fi
sed "$@" file
시험:
$ a=4 b=1 sh script
a
d
b
c
e
$ a=1 b=4 sh script
b
c
d
a
e
ed
편집기를 사용하면 간단히 다음을 사용하여 이 작업을 수행할 수 있습니다 4m1
. "라인 1 뒤에 라인 4를 이동":
$ printf '%s\n' 4m1 ,p Q | ed -s file
a
d
b
c
e
"행 1을 행 4 뒤로 이동":
$ printf '%s\n' 1m4 ,p Q | ed -s file
b
c
d
a
e
위의 셸 스크립트 요약에 따르면 범위를 a,b
줄 끝 c
(즉, a,b m c
의 내용 ed
)으로 이동하려면 다음을 사용하세요 sed
.
# Moves range a,b to after line c
# Corresponds to "a,b m c" in ed(1)
if [ "$a" -gt "$b" ]; then
# Error
echo 'Range is reversed' >&2
exit 1
elif [ "$c" -ge "$a" ] && [ "$c" -lt "$b" ]; then
# Error
echo 'Can not move range to within range' >&2
exit 1
fi
if [ "$b" -eq "$c" ] || [ "$a" -eq "$((c+1))" ]; then
# No move
set -- -e b
else
if [ "$a" -lt "$c" ]; then
# Move forwards
x="$a" y="$b" z="$c"
else
# Move backwards
x="$((c+1))" y="$((a-1))" z="$b"
fi
set -- -e "$x,$y { H; $x h; d; }" -e "$z G"
fi
sed "$@" file
이 답변에서 자세히 다루는 다른 부분과 마찬가지로 스크립트의 필수 부분은 이동 방향을 기반으로 , 및 를 if
설정하는 마지막 내부 명령문입니다. 나머지는 단지 건강 상태를 확인하는 것입니다.x
y
z
답변2
그냥 awk를 사용하세요. 모든 Unix 시스템의 모든 쉘에서 awk를 사용하십시오.
$ awk 'NR==FNR{a[NR]=$0; next} FNR==1{tmp=a[4]; a[4]=a[2]; a[2]=tmp} {print a[FNR]}' file file
a
d
c
b
e
또는 여러 행을 바꾸려면 swap[]
바꾸려는 행 번호 쌍으로 다음 배열을 채우십시오.
awk '
BEGIN { split("2 4 3 5",swap) }
NR==FNR { a[NR]=$0; next }
FNR==1 {
for ( i=1; i in swap; i+=2 ) {
tmp = a[swap[i]]
a[swap[i]] = a[swap[i+1]]
a[swap[i+1]] = tmp
}
}
{ print a[FNR] }
' file file
a
d
e
b
c
답변3
### initialize variables
a=2
b=4
b1=$((b-1))
sed 사용
sed -n ":1
$a,$b{
$b1!{N;b1;}
h;n;G
}
p
" file
Perl 유틸리티 사용
perl -ne "$a..$b-1"' and push(@A,$_) or print($_,splice(@A))' file
awk를 사용하세요
awk -v a="$a" -v b="$b" '
a<=NR && NR<b {x[++i]=$0;next}
{
print
for (n=i; i>0; i--)
print x[n-i+1]
}
' file
Python 사용:
python3 -c 'import sys, itertools as it
ors = rs = "\n"
inp = sys.argv.pop(1)
a,b = map(int,sys.argv[1:])
chomp = lambda x: x.rstrip(rs)
with open(inp) as g:
f = map(chomp,g)
for nr,_ in enumerate(f,1):
if nr < a: print(_)
else: break
G = list(it.islice(f,0,b-a))
print(G.pop(),_,*G,sep=ors)
for _ in f: print(_)
' file "$a" "$b"
GNU 데스크탑 계산기 유틸리티 사용:
< file sed 's/.*/[&]/' |
dc -e "[q]se
[?pc l.1+ds. $a>p]sp
[? l.1+ds. $b>q]sq
[LMpc l*1-ds* 1<r]sr
[?z0=e pc z0=?]s?
[SM z0<w]sw
[
1s. # initialize line counter
lpx # print lines before 2
lqx # store on stack lines 2..4
?zs*p # save stack size n print nxt
lwx # save stack onto register 4..2
lrx # print register contents 2..2
l?x # print remaining lines
]s@
l@x
"