sed를 사용하여 스택과 같은 이동을 통해 사용자 입력에 따라 줄을 이동하는 방법은 무엇입니까?

sed를 사용하여 스택과 같은 이동을 통해 사용자 입력에 따라 줄을 이동하는 방법은 무엇입니까?

문자열 목록이 주어지면:

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-1a

단순히 일련의 줄을 로 이동하는 것으로 표현하면 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설정하는 마지막 내부 명령문입니다. 나머지는 단지 건강 상태를 확인하는 것입니다.xyz

답변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
"

관련 정보