변수에서 문자를 제거하는 가장 짧은 방법

변수에서 문자를 제거하는 가장 짧은 방법

변수에서 문자를 제거하는 방법에는 여러 가지가 있습니다.

지금까지 내가 알아낸 가장 짧은 방법은tr:

OUTPUT=a\'b\"c\`d_123and_a_lot_more
OUTPUT=$(echo "$OUTPUT"|tr -d "'\`\"")
echo $OUTPUT

더 빠른 방법이 있나요?

'그리고 이 인용문은 , "및 그 자체와 같은 인용문에 안전합니까 `?

답변1

어디 보자. 내가 생각해 낼 수 있는 가장 짧은 방법은 솔루션을 조정하는 것입니다 tr.

OUTPUT="$(tr -d "\"\`'" <<<$OUTPUT)"

다른 대안에는 지금까지 표시된 것보다 더 짧을 수 있는 이미 언급된 변수 대체가 포함됩니다.

OUTPUT="${OUTPUT//[\'\"\`]}"

물론 sed이것은 문자 측면에서 더 길지만 다음과 같습니다.

OUTPUT="$(sed s/[\'\"\`]//g <<<$OUTPUT)"

길이가 가장 짧다는 뜻인지, 아니면 소요 시간이 가장 짧다는 뜻인지 잘 모르겠습니다. 길이 면에서 이 두 문자는 해당 특정 문자를 제거할 때 얻을 수 있는 만큼(또는 어쨌든 얻을 수 있는 만큼) 짧습니다. 그렇다면 어느 것이 가장 빠른가요? OUTPUT예제에 있는 변수를 설정하여 테스트했지만 수십 번 반복했습니다.

$ echo ${#OUTPUT} 
4900

$ time tr -d "\"\`'" <<<$OUTPUT
real    0m0.002s
user    0m0.004s
sys     0m0.000s
$ time sed s/[\'\"\`]//g <<<$OUTPUT
real    0m0.005s
user    0m0.000s
sys     0m0.000s
$ time echo ${OUTPUT//[\'\"\`]}
real    0m0.027s
user    0m0.028s
sys     0m0.000s

보시다시피 가 tr확실히 가장 빠르며 그 뒤를 바짝 뒤쫓고 있습니다 sed. 또한 echo실제로는 다음을 사용하는 것보다 약간 더 빠른 것 같습니다 <<<.

$ for i in {1..10}; do 
    ( time echo $OUTPUT | tr -d "\"\`'" > /dev/null ) 2>&1
done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}'; 
0.0025
$ for i in {1..10}; do 
    ( time tr -d "\"\`'" <<<$OUTPUT > /dev/null ) 2>&1 
  done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}'; 
0.0029

차이가 작기 때문에 위의 테스트를 두 테스트 각각에 대해 10번 실행했으며 가장 빠른 테스트는 실제로 시작해야 하는 테스트였습니다.

echo $OUTPUT | tr -d "\"\`'" 

그러나 변수에 할당하는 오버헤드를 고려하면 변경됩니다. 여기서는 tr간단한 교체보다 사용이 약간 느립니다.

$ for i in {1..10}; do
    ( time OUTPUT=${OUTPUT//[\'\"\`]} ) 2>&1
  done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}'; 
0.0032

$ for i in {1..10}; do
    ( time OUTPUT=$(echo $OUTPUT | tr -d "\"\`'")) 2>&1
  done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}'; 
0.0044

따라서 결론적으로 단순히 결과를 보고 싶을 때는 다음을 사용 tr하지만 변수에 다시 할당하려는 경우에는 별도의 하위 쉘을 실행하는 오버헤드를 피하기 때문에 쉘의 문자열 조작 기능을 사용하는 것이 더 빠릅니다.

답변2

당신은 사용할 수 있습니다변수 대체:

$ OUTPUT=a\'b\"c\`d
$ echo "$OUTPUT"
a'b"c`d

해당 구문을 사용하여 ${parameter//pattern/string}패턴의 모든 항목을 문자열로 바꾸십시오.

$ echo "${OUTPUT//\'/x}"
axb"c`d
$ echo "${OUTPUT//\"/x}"
a'bxc`d
$ echo "${OUTPUT//\`/x}"
a'b"cxd
$ echo "${OUTPUT//[\'\"\`]/x}"
axbxcxd

답변3

Bash 또는 zsh에서는 다음과 같습니다.

OUTPUT="${OUTPUT//[\`\"\']/}"

${VAR//PATTERN/}패턴의 모든 인스턴스를 제거합니다 . 자세한 내용은Bash 매개변수 확장

이 솔루션은 외부 프로그램 실행을 포함하지 않으므로 짧은 문자열의 경우 가장 빠릅니다. 그러나 매우 긴 문자열의 경우 그 반대가 적용됩니다. 예를 들어 텍스트 작업을 위한 전용 도구를 사용하는 것이 더 좋습니다.

$ OUTPUT="$(cat /usr/src/linux/.config)"

$ time (echo $OUTPUT | OUTPUT="${OUTPUT//set/abc}")
real    0m1.766s
user    0m1.681s
sys     0m0.002s

$ time (echo $OUTPUT | sed s/set/abc/g >/dev/null)
real    0m0.094s
user    0m0.078s
sys     0m0.006s

답변4

혹시라도 셸에서 재사용을 위해 인용문을 처리하려는 경우에는 다음과 같이 할 수 있습니다.없이그것들을 제거하면 매우 간단합니다.

aq() { sh -c 'for a do
       alias "$((i=$i+1))=$a"
       done; alias' -- "$@"
}

이 함수 쉘은 전달한 인수 배열을 인용하고 반복 가능한 인수마다 출력을 증가시킵니다.

여기에 몇 가지 인수가 있습니다.

aq \
"here's an
ugly one" \
"this one is \$PATHpretty bad, too" \
'this one```****```; totally sucks'

산출

1='here'"'"'s an
ugly one'
2='this one is $PATHpretty bad, too'
3='this one```****```; totally sucks'

해당 출력은 dash일반적으로 '"'"'. bash할것이다 '\''.

공백이 아니고 null이 아닌 단일 바이트를 다른 단일 바이트로 바꾸는 것은 $IFS및 를 사용하는 모든 POSIX 셸에서 가장 빠르게 수행될 수 있습니다 $*.

set -f; IFS=\"\'\`; set -- $var; printf %s "$*"

산출

"some ""crazy """"""""string ""here

여기에서는 printf여러분이 볼 수 있도록 작성했습니다. 물론, 제가 수행했다면 다음과 같습니다.

var="$*"

printf... 명령 의 값 이 아니라 $var출력에 표시되는 값이 됩니다.

set -f내가 쉘에게 지시할 때~ 아니다to glob - 문자열에 glob 패턴으로 해석될 수 있는 문자가 포함된 경우. 쉘 파서가 글로브 패턴을 확장하기 때문에 이렇게 합니다.~ 후에변수에 대해 필드 분할을 수행합니다. globbing은 다음과 같이 다시 활성화할 수 있습니다 set +f. 일반적으로 스크립트에서 다음과 같이 설정하는 것이 유용하다고 생각합니다.

#!/usr/bin/sh -f

그리고 나서명시적으로 글로빙을 활성화합니다.set +f내가 원하는 어떤 라인에서든 .

필드 분할은 의 문자를 기준으로 발생합니다 $IFS.

$IFS값 에는 $IFS공백과 $IFS공백이 아닌 두 가지 종류가 있습니다 . $IFS공백(공백, 탭, 개행)구분된 필드는 다음을 통해 제거하도록 지정됩니다.순서단일 필드로(또는 다른 것보다 앞에 있지 않으면 전혀 없음)- 그래서...

IFS=\ ; var='      '; printf '<%s>' $var
<>

그러나 다른 모든 항목은 단일 필드로 평가되도록 지정되었습니다.발생당- 잘리지 않습니다.

IFS=/; var='/////'; printf '<%s>' $var
<><><><><>

모두변수 확장은 기본적으로 $IFS구분된 데이터 배열입니다. 에 따라 별도의 필드로 분할됩니다 $IFS. 하나를 인용 하면 "해당 배열 속성을 재정의하고 단일 문자열로 평가합니다.

그래서 내가 할 때 ...

IFS=\"\'\`; set -- $var

나는 셸의 인수 배열을 의 확장 으로 생성된 많은 $IFS구분 필드 로 설정하고 있습니다. $var확장되면 포함된 문자의 구성 값은 $IFS다음과 같습니다.잃어버린- 지금은 필드 구분 기호일 뿐입니다. 입니다 \0NUL.

"$*"- 다른 큰따옴표로 묶인 변수 확장과 마찬가지로 - 의 필드 분할 특성도 무시합니다 $IFS. 하지만,게다가, 첫 번째 바이트를 대체합니다.$IFS 구분된 각 필드에 대해안에 "$@". 그래서 "그랬기 때문에첫 번째가치를 부여하다$IFS 모든 후속 구분 기호는 가 "됩니다 "$*".그리고 분할할 때도 "포함될 필요가 없습니다 . $IFS당신은 변경할 수 있습니다$IFS ~ 후에 set -- $args완전히 다른 값으로 변환하고새로운그러면 첫 번째 바이트가 의 필드 구분 기호로 표시됩니다 "$*". 또한 다음과 같이 모든 흔적을 완전히 제거할 수 있습니다.

set -- $var; IFS=; printf %s "$*"

산출

some crazy string here

관련 정보