sh 스크립트의 변수를 파일 기본 이름의 마지막 3자로 설정하려고 합니다(기본 이름은 경로가 없음을 의미함).그리고접미사 없음). 이 작업을 성공적으로 수행했지만 순수한 호기심으로 사용할 수 있는 더 짧은 단일 명령이 있는지 알고 싶습니다. 원래는 한줄짜리 awk
였는데 꽤 길었습니다. 현재 다음 두 줄의 스크립트가 있습니다(전체 파일 이름이 있다고 가정 $1
).
filebase=`basename "$1"`
lastpart=`echo -n ${filebase%.*} | tail -c3`
예를 들어,"/경로/to/somefile.txt"마지막으로"엘리"존재하다 $lastpart
.
어떻게든 및 비트를 결합하여 basename
접미사를 단일 명령으로 제거할 수 있습니까? tail
파이프를 사용하지 않고 접미사(또는 사용할 수 있는 다른 항목)로 보낼 수 있는 방법이 있습니까? 접미사를 알 수 없어 매개변수로 전달할 수 없습니다 basename
.
실제로 주요 목표는 가능한 한 짧게 작성하는 것이 아니라 최대한 명확하게 작성하는 것입니다. 이 모든 것의 실제 배경은 다음과 같습니다.슈퍼유저에 대한 질문입니다., 나는 아주 간단한 대답을하려고 노력했습니다.
답변1
var=123456
echo "${var#"${var%???}"}"
###OUTPUT###
456
먼저 마지막 세 문자를 제거한 다음 $var
해당 제거 결과에서 마지막 세 문자를 제거합니다. 다음은 이와 같은 작업을 수행하는 방법을 보여주기 위해 고안된 몇 가지 구체적인 예입니다.$var
$var
touch file.txt
path=${PWD}/file.txt
echo "$path"
/tmp/file.txt
base=${path##*/}
exten=${base#"${base%???}"}
base=${base%."$exten"}
{
echo "$base"
echo "$exten"
echo "${base}.${exten}"
echo "$path"
}
file
txt
file.txt
/tmp/file.txt
너무 많은 명령을 사용하여 모든 것을 퍼뜨릴 필요는 없습니다. 다음과 같이 압축할 수 있습니다.
{
base=${path##*/} exten=
printf %s\\n "${base%.*}" "${exten:=${base#"${base%???}"}}" "$base" "$path"
echo "$exten"
}
file
txt
file.txt
/tmp/file.txt
txt
$IFS
ting 쉘 매개변수와 결합하면 set
쉘 변수를 구문 분석하고 드릴링하는 매우 효과적인 수단이 될 수도 있습니다.
(IFS=. ; set -f; set -- ${path##*/}; printf %s "${1#"${1%???}"}")
/
이렇게 하면 의 마지막 마침표 뒤 첫 번째 마침표 바로 앞의 3개 문자만 제공됩니다 $path
. .
마지막 문자 이전의 처음 세 문자 만 검색하려는 경우$path
.
(예: 파일 이름에 둘 이상이 있을 수 있는 경우):
(IFS=.; set -f; set -- ${path##*/}; ${3+shift $(($#-2))}; printf %s "${1#"${1%???}"}")
두 경우 모두 다음을 수행할 수 있습니다.
newvar=$(IFS...)
그리고...
(IFS...;printf %s "$2")
...다음을 인쇄합니다.
외부 프로그램을 사용해도 괜찮다면 다음을 수행할 수 있습니다.
printf %s "${path##*/}" | sed 's/.*\(...\)\..*/\1/'
\n
파일 이름에 줄바꿈 문자가 있을 수 있는 경우(네이티브 쉘 솔루션에서는 작동하지 않습니다 - 어쨌든 처리합니다):
printf %s "${path##*/}" | sed 'H;$!d;g;s/.*\(...\)\..*/\1/'
답변2
일반적인 작업은 다음과 같습니다 expr
.
$ file=/path/to/abcdef.txt
$ expr "/$file" : '.*\([^/.]\{3\}\)\.[^/.]*$'
def
파일 이름이 예상되는 형식(점은 하나만 포함하고 점 앞에 최소 3개의 문자가 포함됨)이 있다는 것을 알고 있는 경우 다음과 같이 단순화할 수 있습니다.
expr "/$file" : '.*\(.\{3\}\)\.'
일치하는 항목이 없으면 종료 상태는 0이 아니며 일치 부분이 0으로 확인되는 숫자인 경우에 유의하세요. (예: for a000.txt
또는 a-00.txt
)
그리고 zsh
:
file=/path/to/abcdef.txt
lastpart=${${file:t:r}[-3,-1]}
( :t
을 위한꼬리(기본 이름 :r
)나머지(확장자 제거)).
답변3
사용할 수 있는 경우 perl
:
lastpart=$(
perl -e 'print substr((split(/\.[^.]*$/,shift))[0], -3, 3)
' -- "$(basename -- "$1")"
)
답변4
Perl을 사용할 수 있는 경우 다른 솔루션보다 더 읽기 쉽다고 생각합니다. 특히 Perl의 정규식 언어가 더 표현력이 뛰어나고 /x
더 명확한 정규식을 작성할 수 있는 수정자가 있기 때문입니다.
perl -e 'print $1 if shift =~ m{ ( [^/]{3} ) [.] [^./]* \z }x' -- "$file"
일치하는 항목이 없으면(기본 이름에 확장자가 없거나 확장자 앞의 루트가 너무 짧은 경우) 아무 것도 인쇄되지 않습니다. 요구 사항에 따라 정규식을 조정할 수 있습니다. 이 정규식은 제약 조건을 적용합니다.
- 최종 확장자 앞의 3개 문자(마지막 점을 포함한 뒤 부분)와 일치합니다. 이 3개 문자에는 점이 포함될 수 있습니다.
- 확장자는 비어 있을 수 있습니다(점 제외).
- 일치하는 부분과 확장자는 기본 이름(마지막 슬래시 다음 부분)의 일부여야 합니다.
명령 대체에 이를 사용하면 후행 개행 문자가 너무 많이 제거되는 일반적인 문제가 발생하며 이는 Stéphane의 답변에도 영향을 미칩니다. 두 경우 모두 처리할 수 있지만 여기가 더 쉽습니다.
lastpart=$(
perl -e 'print "$1x" if shift =~ m{ ( [^/]{3} ) [.] [^./]* \z }x' -- "$file"
)
lastpart=${lastpart%x} # allow for possible trailing newline