sed 표현식에 쉘 명령을 포함시키는 방법은 무엇입니까?


16

다음 형식의 텍스트 파일이 있습니다.

keyword value
keyword value
...

여기서 키워드는 한 단어이며 값은 줄 끝까지의 모든 것입니다. 키워드가 아닌 값이 쉘 확장을 수행하는 방식으로 쉘 스크립트에서 파일을 읽고 싶습니다.

sed를 사용하면 키워드와 가치 부분을 쉽게 일치시킬 수 있습니다

input='
keyword value value
keyword "value  value"
keyword `uname`
'

echo "$input"|sed -e 's/^\([^[:space:]]*\)[[:space:]]\(.*\)$/k=<\1> v=<\2>/'

어떤 생산

k=<keyword> v=<value value>
k=<keyword> v=<"value  value">
k=<keyword> v=<`uname`>

그러나 질문은 쉘 명령을 sed 표현식의 대체 부분에 포함시키는 방법입니다. 이 경우 교체를하고 싶습니다 \1 `echo \2`.


음 ... 나는 그것을 대답으로 확신하지는 않지만 sed로 인용 된 DOUBLE을 사용하면 표현식 내에서 쉘 $ (command) 또는 $ variables를 사용할 수 있어야합니다.
St0rM

답변:


18

표준 sed는 쉘을 호출 할 수 없습니다 ( GNU sed에는 비 임베디드 Linux 만 신경 쓰는 경우 확장 기능이 있습니다 ). sed 외부에서 일부 처리를 수행해야합니다. 몇 가지 해결책이 있습니다. 모두 신중하게 인용해야합니다.

값을 어떻게 확장할지 정확히 알 수는 없습니다. 예를 들어 라인이

foo hello; echo $(true)  3

다음 중 어느 것이 출력되어야합니까?

k=<foo> value=<hello; echo   3>
k=<foo> value=<hello; echo   3>
k=<foo> value=<hello; echo 3>
k=<foo> value=<foo hello
  3>

아래 몇 가지 가능성에 대해 설명하겠습니다.

순수한 껍질

쉘이 입력을 한 줄씩 읽고 처리하도록 할 수 있습니다. 이것은 가장 간단한 솔루션이며 짧은 파일의 경우 가장 빠릅니다. 이것은 귀하의 요구 사항“ echo \2”에 가장 가까운 것입니다 .

while read -r keyword value; do
  echo "k=<$keyword> v=<$(eval echo "$value")>"
done

read -r keyword value세트 $keyword최초의 공백으로 구분 된 라인의 단어와에 $value선 마이너스 후행 공백의 나머지.

변수 참조를 확장하지만 명령 대체 외부에서 명령을 실행하지 않으려면 here document$value 안에 넣으 십시오 . 나는 이것이 당신이 정말로 찾고있는 것이라고 생각합니다.

while read -r keyword value; do
  echo "k=<$keyword> v=<$(cat <<EOF
$value
EOF
)>"
done

껍질에 파이프 sed

입력을 쉘 스크립트로 변환하고 평가할 수 있습니다. Sed는 쉽지는 않지만 작업에 달려 있습니다. " echo \2"요구 사항에 따라 (키워드에서 작은 따옴표를 이스케이프해야 함) :

sed  -e 's/^ *//' -e 'h' \
     -e 's/[^ ]*  *//' -e 'x' \
     -e 's/ .*//' -e "s/'/'\\\\''/g" -e "s/^/echo 'k=</" \
     -e 'G' -e "s/\n/>' v=\\</" -e 's/$/\\>/' | sh

here 문서와 함께 키워드를 이스케이프 처리해야합니다 (그러나 다르게).

{
  echo 'cat <<EOF'
  sed -e 's/^ */k=</' -e 'h' \
      -e 's/[^ ]*  *//' -e 'x' -e 's/ .*//' -e 's/[\$`]/\\&/g' \
      -e 'G' -e "s/\n/> v=</" -e 's/$/>/'
  echo 'EOF'
 } | sh

데이터가 많은 경우 가장 빠른 방법입니다. 각 라인마다 별도의 프로세스를 시작하지 않습니다.

어 wk

sed 작업에 awk를 사용한 것과 동일한 기술. 결과 프로그램은 훨씬 더 읽기 쉽습니다. "로 이동echo \2 "로 이동 :

awk '
  1 {
      kw = $1;
      sub(/^ *[^ ]+ +/, "");
      gsub(/\047/, "\047\\\047\047", $1);
      print "echo \047k=<" kw ">\047 v=\\<" $0 "\\>";
  }' | sh

here 문서 사용하기 :

awk '
  NR==1 { print "cat <<EOF" }
  1 {
      kw = $1;
      sub(/^ *[^ ]+ +/, "");
      gsub(/\\\$`/, "\\&", $1);
      print "k=<" kw "> v=<" $0 ">";
  }
  END { print "EOF" }
' | sh

좋은 대답입니다. 입력 파일이 작고 성능이 중요하지 않으므로 깨끗하고 읽기 쉬운 순수한 쉘 솔루션을 사용하려고합니다.
어니스트 AC

약간의 해킹이지만 충분히 깔끔합니다. 예를 들어 sed를 사용하여 긴 16 진 문자열을 디코딩하기 위해 xxd를 호출하십시오. . . 고양이 FtH.ch13 | sed -r 's /(.* text. * : [) ([0-9a-fA-F] *)] / \ 1 $ (echo \ 2 | xxd -r -p)] /; s / ^ ( . *) $ / echo "\ 1"/ g '| bash> FtHtext.ch13 여기서 FtH.ch13에는 "foo bar hex text test : [666f6f0a62617200]"와 같은 행이 있습니다.
gaoithe

14

GNU sed를 사용하면 다음 명령을 사용할 수 있습니다.

sed -nr 's/([^ ]+) (.*)/echo "\1" \2\n/ep' input

어떤 출력 :

keyword value value
keyword value  value
keyword Linux

입력 데이터와 함께.

설명:

sed 명령은 -n옵션을 사용하여 일반 출력을 억제합니다 . -r확장 정규 표현식을 사용하도록 전달되어 패턴에서 특수 문자를 이스케이프 처리 할 수는 있지만 필수는 아닙니다.

s명령은 입력 행을 명령으로 전송하는 데 사용됩니다.

echo "\1" \2

키워드는 값을 인용하지 않습니다. sed에게 대체 결과를 쉘 명령으로 실행하고 결과를 패턴 버퍼 (여러 행)로 읽도록 지시 e하는 s명령에 GNU 고유 의 옵션을 전달합니다 . 옵션 사용 p후하는 (!) e하게 sed명령이 실행 된 후 패턴 버퍼를 인쇄.


-np옵션 을 모두 사용하지 않고도 할 수 있습니다 sed -r 's/([^ ]+) (.*)/echo "\1" \2\n/e' input. 그러나 이것에 감사드립니다! 나는 e옵션 에 대해 몰랐다 .
Kaushal Modi

@KaushalModi 아, 맞아요! e옵션에 관해서는 울타리에 앉아 있습니다 (GNU에서 소개). 아직도 sed그래요? :)
hek2mgl 17

글쎄, 그것은 나를 위해 일했다. RHEL 배포에서 기본적으로 GNU sed (GNU sed 버전 4.2.1)입니다.
카우 살 모디

4

이 방법을 시도해 볼 수 있습니다.

input='
keyword value value
keyword "value  value"
keyword `uname`
'

process() {
  k=$1; shift; v="$*"
  printf '%s\n' "k=<$k> v=<$v>"
}

eval "$(printf '%s\n' "$input" | sed -n 's/./process &/p')"

(당신의 의도가 맞다면). 비어 있지 않은 각 줄의 시작 부분에 "process"를 삽입하여 다음과 같은 스크립트로 만듭니다.

process keyword value value
process keyword "value  value"
process keyword `uname`

평가 (eval ) 여기서 처리는 예상되는 메시지를 출력하는 기능이다.


1

sed가 아닌 솔루션이 허용 가능한 경우이 PERL 스 니펫이 다음 작업을 수행합니다.

$ echo "$input" | perl -ne 'chomp; /^\s*(.+?)\s+(.+)$/ && do { $v=`echo "$2"`; chomp($v); print "k=<$1> v=<$v>\n"}'

1
고맙지 만 표준 유닉스 명령어와 bourne shell을 유지할 수 있다면 다른 스크립트 언어를 사용하지 않는 것이 좋습니다.
어니스트 AC

0

키스 숏 퓨어 SED 만

난 이걸 할거야

echo "ls_me" | sed -e "s/\(ls\)_me/\1/e" -e "s/to be/continued/g;"

그리고 그것은 작동합니다.


어떻게 작동하는지 설명해 주시겠습니까?
elysch
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.