변수에서 문자를 바꾸는 가장 짧은 방법


18

변수에서 문자를 바꾸는 방법에는 여러 가지가 있습니다.

내가 찾은 가장 짧은 방법은 tr지금까지입니다.

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

더 빠른 방법이 있습니까? 그리고 같은 인용이 인용 안전이다 ', "그리고 '그 자체?


계속 사용할 수 있다고 생각합니다 tr. BASH의 PE는 좋지만이 경우 tr이 훨씬 빠릅니다. 예를 들어, echo "$OUTPUT" | tr -dc '[[:alpha:]]' 당신은 단지 영숫자 갖고 싶어하기 때문에
발렌틴 Bajrami

2
인용에 정통한 것에 관심이 있기 때문에 항상 변수를 인용하십시오! echo "$OUTPUT". 또는 더 나은 : printf "%s\n" "$OUTPUT". (무슨 일이 일어 날까 OUTPUT="-n"?)
musiphil

codegolf 탐색 , 특히 bash 팁 탐색을 고려할 수도 있습니다 .
hoosierEE

답변:


22

보자 내가 생각해 낼 수있는 가장 짧은 방법은 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하지만 변수에 재 할당하려면 쉘의 문자열 조작 기능을 사용하는 것이 별도의 서브 쉘을 실행하는 오버 헤드를 피하기 때문에 더 빠릅니다.


4
영업 이익이로 수정 된 값을 다시 설정에 관심이 있기 때문에 OUTPUT, 당신은 오버 헤드에 관련된 명령 치환 서브 쉘 계정에있을 것이다 trsed솔루션
iruvar

@ 1_CR 예. 그것이 그가 사용하는 방법이 될 것이기 때문에 나는 그것이 관련이 없다고 생각했습니다.
terdon

1
확실히, OUTPUT="${OUTPUT//[`\"\']/}" 명령 대체를 포함하지 않습니다
iruvar

@ 1_CR 아, 그렇습니다, 당신이 옳고 그로 인해 결과가 변경됩니다. 감사합니다. 답변이 수정되었습니다.
terdon

2
명령 대체와 관련된 메소드에는 문자열을 약간 조작하는 단점이 있습니다. (이를 피할 수는 있지만 명령을 훨씬 더 복잡하게 만들 수 있습니다.) 특히 명령 대체는 후행 줄 바꿈을 제거합니다.
Gilles 'SO- 악마 그만해

15

변수 대체를 사용할 수 있습니다 .

$ 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

@ rubo77 echo ${OUTPUT//[`\"\']/x}제공axbxcxa
혼돈

확장의 이름을 "가변 확장"으로 잘못 지정했습니다. 이것을 「파라미터 확장」이라고합니다.
gena2x

@ gena2x-귀하의 의견이 여기에서 의미하는 바를 모르겠습니다
slm

12

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

1
실제로 tr더 빠릅니다. 정규 표현식과 globs는 비싸며 여기에는 외부 프로그램이 없지만 bash는 항상와 같은 것보다 느립니다 tr.
terdon

이는 입력 데이터와 정규 표현식 구현에 크게 의존합니다. 귀하의 답변에 특정 큰 데이터 세트를 사용했지만 데이터 세트는 작을 수 있습니다. 아니면 다릅니다. 또한 정규 표현식 시간이 아닌 에코 시간을 측정하므로 비교가 정말 공정한지 확신 할 수 없습니다.
gena2x

좋은 지적입니다. 그러나 테스트 없이는 속도에 대한 주장을 할 수 없습니다. 실제로 변수에 할당 할 때 더 빠르지 만 화면에 인쇄하면 tr승리합니다 (내 답변 참조). 나는 그것이 많은 요소에 달려 있다는 데 동의하지만 실제로 테스트하지 않고 어느 쪽이 이길 수 있는지 정확히 알 수없는 이유입니다.
terdon

6

오프 챈스에서 쉘을 재사용하기 위해 따옴표를 처리하려고하면 따옴표를 제거 하지 않고도이 작업을 수행 할 수 있습니다. 죽은 것도 간단합니다.

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

이 함수 쉘은 사용자가 건네주는 arg 배열을 인용하고 반복 가능한 인수마다 출력을 증가시킵니다.

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

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 이외의 바이트 가능성이 어떤 POSIX 쉘에서 가장 빠른을 수행 할 수 있습니다 $IFS$*.

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

산출

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

거기에서 나는 printf그것을 볼 수 있도록하지만, 내가 한 경우 물론 :

var="$*"

... printf명령 $var값 대신 출력에 표시됩니다.

set -f쉘 에 glob 하지 말라고 지시 할 때 -문자열에 glob 패턴으로 해석 될 수있는 문자가 포함되어있는 경우. 쉘 파서 는 변수에서 필드 분할을 수행 한 후 glob 패턴 확장하기 때문에이 작업을 수행합니다. 글 로빙은 다음과 같이 다시 활성화 할 수 있습니다 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입니다. 에 따라 필드가 분리됩니다 . "1 을 인용하면 해당 배열 속성을 재정의하고 단일 문자열로 평가합니다.

그래서 내가 할 때 ...

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

쉘의 인수 배열을 의 확장에 $IFS의해 생성 된 많은 구분 된 필드로 설정하고 $var있습니다. 확장되면 포함 된 문자의 구성 값 $IFS손실 됩니다 \0NUL. 현재 필드 구분자 일뿐입니다 .

"$*"-다른 큰 따옴표로 묶은 변수 확장과 마찬가지로-의 필드 분할 품질도 무시합니다 $IFS. 그러나 또한 $IFS 구분 된 각 필드대해 첫 번째 바이트를 대체합니다 "$@". 따라서 모든 후속 분리 문자"첫 번째 값이에 있기 때문 입니다 . 그리고 필요에 있지 하거나, 당신이 그것을 분할 할 때. 당신은 변경할 수 다른 값으로 완전히 그 새로운 첫 번째 바이트는 다음의 필드 구분 기호 올라올 것입니다 . 또한 다음과 같은 흔적을 모두 제거 할 수 있습니다.$IFS ""$*""$IFS$IFS set -- $args"$*"

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

산출

some crazy string here

아주 좋아요, +1 나는 그것이 더 빠른 지 궁금합니다. 내 답변의 접근법과 비교하여 타이밍 테스트를 추가 할 수 있습니까? 나는 당신이 더 빠를 것으로 기대하지만보고 싶습니다.
terdon

@terdon-쉘에 따라 다릅니다. 그것은이다 거의 확실히 보다 더 빨리 tr어떤 쉘하지만, 차이는에 불확실하다 bash위한 ${var//$c/$newc/}경우. 나는이 경우에도 약간의 마진으로 더 빠를 것으로 예상하지만, dash일반적으로 모든 것을 고려할 때이 크기 때문에 항상 사용하기 때문에 걱정하지 않습니다 . 그리고 비교하기가 어렵습니다.
mikeserv

@ terdon-시도했습니다. 그러나- bash수행 중이 time (IFS=\"\'`; set -- $var; printf %s "$*")거나 time (var=${var//\'`/\"/})두 가지 모두 0.0000s모든 필드에 대한 결과를 초래합니다. 내가 뭔가 잘못하고 있다고 생각합니까? 백 따옴표 앞에 백 슬래시가 있어야하지만 주석 코드 필드에 백 따옴표를 넣는 방법을 몰라요.
mikeserv
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.