이전 버전의 busybox를 실행하는 OpenWRT 장치에서 쉘 스크립트를 사용하여 문자열을 URL 인코딩하는 방법이 필요합니다. 이제 다음 코드를 얻었습니다.
urlencode() {
echo "$@" | awk -v ORS="" '{ gsub(/./,"&\n") ; print }' | while read l
do
c="`echo "$l" | grep '[^-._~0-9a-zA-Z]'`"
if [ "$l" == "" ]
then
echo -n "%20"
else
if [ -z "$c" ]
then
echo -n "$l"
else
printf %%%02X \'"$c"
fi
fi
done
echo ""
}
이는 다소 잘 작동하지만 몇 가지 결함이 있습니다.
- "\"와 같은 특정 문자는 건너뜁니다.
- 결과는 한 문자씩 반환되므로 매우 느립니다. 일괄 처리에서 몇 개의 문자열만 URL 인코딩하는 데 약 20초가 걸립니다.
내 bash 버전은 ${var:x:y}와 같은 하위 문자열을 지원하지 않습니다.
답변1
[TL,DR: urlencode_grouped_case
마지막 코드 블록의 버전을 사용하세요. ]
awk는 대부분의 작업을 수행할 수 있지만 짜증나게도 문자를 숫자로 변환하는 방법이 부족합니다. od
장치에 존재하는 경우 이를 사용하여 모든 문자(보다 정확하게는 바이트)를 해당 숫자(awk가 읽을 수 있도록 10진수로 기록됨)로 변환한 다음 awk를 사용하여 유효한 문자를 다시 리터럴로 변환하고 인용된 문자를 올바른 형식으로 변환합니다.
urlencode_od_awk () {
echo -n "$1" | od -t d1 | awk '{
for (i = 2; i <= NF; i++) {
printf(($i>=48 && $i<=57) || ($i>=65 && $i<=90) || ($i>=97 && $i<=122) ||
$i==45 || $i==46 || $i==95 || $i==126 ?
"%c" : "%%%02x", $i)
}
}'
}
장치에 가 없으면 od
셸 내부에서 모든 작업을 수행할 수 있습니다. 이렇게 하면 성능이 크게 향상되고(외부 프로그램에 대한 호출 수가 줄어들고 printf
내장 프로그램인 경우 없음) 올바르게 작성하기가 더 쉬워집니다. 나는 모든 Busybox 쉘이 ${VAR#PREFIX}
문자열에서 접두어를 잘라내는 구문을 지원한다고 생각합니다. 이를 사용하여 문자열의 첫 번째 문자를 반복적으로 제거합니다.
urlencode_many_printf () {
string=$1
while [ -n "$string" ]; do
tail=${string#?}
head=${string%$tail}
case $head in
[-._~0-9A-Za-z]) printf %c "$head";;
*) printf %%%02x "'$head"
esac
string=$tail
done
echo
}
printf
내장 유틸리티가 아니고 외부 유틸리티인 경우 문자당 한 번이 아닌 전체 기능을 한 번만 호출하면 다시 성능을 얻을 수 있습니다 . 형식과 매개변수를 작성한 다음 printf
.
urlencode_single_printf () {
string=$1; format=; set --
while [ -n "$string" ]; do
tail=${string#?}
head=${string%$tail}
case $head in
[-._~0-9A-Za-z]) format=$format%c; set -- "$@" "$head";;
*) format=$format%%%02x; set -- "$@" "'$head";;
esac
string=$tail
done
printf "$format\\n" "$@"
}
외부 호출에 관한 한 이는 최적입니다(호출은 하나만 있으며 이스케이프해야 하는 모든 문자를 기꺼이 열거하지 않는 한 순수 쉘 구성으로는 이를 수행할 수 없습니다). 매개변수의 문자 대부분을 변경하지 않고 전달해야 하는 경우 이를 일괄 처리할 수 있습니다.
urlencode_grouped_literals () {
string=$1; format=; set --
while
literal=${string%%[!-._~0-9A-Za-z]*}
if [ -n "$literal" ]; then
format=$format%s
set -- "$@" "$literal"
string=${string#$literal}
fi
[ -n "$string" ]
do
tail=${string#?}
head=${string%$tail}
format=$format%%%02x
set -- "$@" "'$head"
string=$tail
done
printf "$format\\n" "$@"
}
컴파일 옵션에 따라 [
(일명 test
)은 외부 유틸리티일 수 있습니다. 문자열 일치에만 사용하며 구문을 사용하여 셸에서도 수행할 수 있습니다 case
. test
내장 메소드를 피하기 위해 재정의된 마지막 두 메소드는 문자별로 먼저입니다:
urlencode_single_fork () {
string=$1; format=; set --
while case "$string" in "") false;; esac do
tail=${string#?}
head=${string%$tail}
case $head in
[-._~0-9A-Za-z]) format=$format%c; set -- "$@" "$head";;
*) format=$format%%%02x; set -- "$@" "'$head";;
esac
string=$tail
done
printf "$format\\n" "$@"
}
그리고 각 텍스트 필드를 일괄 복사합니다.
urlencode_grouped_case () {
string=$1; format=; set --
while
literal=${string%%[!-._~0-9A-Za-z]*}
case "$literal" in
?*)
format=$format%s
set -- "$@" "$literal"
string=${string#$literal};;
esac
case "$string" in
"") false;;
esac
do
tail=${string#?}
head=${string%$tail}
format=$format%%%02x
set -- "$@" "'$head"
string=$tail
done
printf "$format\\n" "$@"
}
내 라우터(MIPS 프로세서, DD-WRT 기반 배포판, printf
Ash가 포함된 BusyBox, 외부 및 [
. 각 버전은 이전 버전에 비해 속도가 크게 향상되었습니다. 단일 포크로 이동하는 것은 가장 중요한 개선 사항입니다. 이는 몇 초 후에 실제 긴 URL 매개변수에 응답하는 대신 함수가 거의 즉각적으로(사람의 관점에서) 응답하도록 만듭니다.
위의 코드는 이국적인 지역에서는 실패할 수 있습니다(라우터에서는 거의 발생하지 않음). export LC_ALL=C
기본이 아닌 로케일을 사용하는 경우 이는 필요할 수 있습니다.
답변2
busybox
유틸리티는 일반적으로 해당 POSIX 유틸리티의 버전을 제거한 반면 (종종 GNU 확장 포함), busybox는 실제로 확장 기능이 포함된 경우에도 awk
거의 호환되는 POSIX API의 본격적인 버전입니다 .awk
awk
따라서 한 번의 호출로 모든 작업을 수행 할 수 있어야 합니다 .
urlencode() {
LC_ALL=C awk -- '
BEGIN {
for (i = 1; i <= 255; i++) hex[sprintf("%c", i)] = sprintf("%%%02X", i)
}
function urlencode(s, c,i,r,l) {
l = length(s)
for (i = 1; i <= l; i++) {
c = substr(s, i, 1)
r = r "" (c ~ /^[-._~0-9a-zA-Z]$/ ? c : hex[c])
}
return r
}
BEGIN {
for (i = 1; i < ARGC; i++)
print urlencode(ARGV[i])
}' "$@"
}
그러면 각 매개변수의 URL 인코딩이 별도의 출력 줄에 인쇄됩니다.
답변3
게시물을 기반으로여기, 보다 우아한 솔루션:
(가벼우면서도 유연한 명령줄 JSON 프로세서)와 Perl이 설치되어 있는 경우 jq
다음 방법을 사용할 수 있습니다.참고하세요-n
명령 매개변수 echo
:
url="http://example.com/resource?param1=value¶m2=foo/bar"
encoded_url=$(echo -n "$url" | jq -s -R -r @uri)