sh 스크립트에서 변수를 파일의 기본 이름의 마지막 3 자로 설정하려고합니다 (기본 이름은 경로 가 없고 접미사가 없음을 의미 합니다). 나는 이것을하는 데 성공했지만 순전히 호기심에서 사용할 수있는 더 짧은 단일 명령이 있는지 궁금합니다. 원래와 함께 하나의 라이너가 awk
있었지만 다소 길었습니다. 현재이 두 줄 스크립트가 있습니다 (완전한 파일 이름이 있다고 가정 $1
).
filebase=`basename "$1"`
lastpart=`echo -n ${filebase%.*} | tail -c3`
예를 들어 “/path/to/somefile.txt” 는에서 “ile” 로 끝납니다 $lastpart
.
어떻게 든 basename
접미사를 단일 명령으로 묶기 위해 비트와 비트를 결합 할 수 있습니까 tail
? 파이프를 사용하지 않고 접미사를 보내 거나 사용할 수 있는 방법이 있습니까? 접미사를 알 수 없으므로에 매개 변수로 사용할 수 없습니다 basename
.
주요 목표는 실제로 가능한 한 짧게 읽을 수있을 정도로 짧지는 않습니다. 이 모든 실제 상황은 수퍼 유저에 대한이 질문입니다 . 여기서 나는 간단한 대답을 제시하려고합니다.
답변
그것은 전형적인 직업입니다 expr
.
$ file=/path/to/abcdef.txt
$ expr "/$file" : '.*\([^/.]\{3\}\)\.[^/.]*$'
def
파일 이름의 형식이 예상되는 경우 (점 앞에 하나의 점 하나와 최소한 3 자 포함) 다음과 같이 단순화 할 수 있습니다.
expr "/$file" : '.*\(.\{3\}\)\.'
일치하는 항목이없는 경우에도 종료 상태는 0이 아니며 일치하는 부분이 0으로 해석되는 숫자 인 경우도 있습니다 (예 : a000.txt
또는 a-00.txt
).
로 zsh
:
file=/path/to/abcdef.txt
lastpart=${${file:t:r}[-3,-1]}
( :t
대한 꼬리 (기본 이름) :r
에 대한 나머지 (확장 제거와 함께)).
답변
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
과 set
팅 쉘 파라미터도 파싱 쉘 변수 통해 시추 매우 효과적인 수단이 될 수있다 :
(IFS=. ; set -f; set -- ${path##*/}; printf %s "${1#"${1%???}"}")
그러면 마지막 마침표 바로 다음의 첫 번째 마침표 바로 앞의 세 문자 만 표시 /
됩니다 $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/'
답변
사용할 수있는 경우 perl
:
lastpart=$(
perl -e 'print substr((split(/\.[^.]*$/,shift))[0], -3, 3)
' -- "$(basename -- "$1")"
)
답변
sed
이것을 위해 작동합니다 :
[user@host ~]$ echo one.two.txt | sed -r 's|(.*)\..*$|\1|;s|.*(...)$|\1|'
two
또는
[user@host ~]$ sed -r 's|(.*)\..*$|\1|;s|.*(...)$|\1|' <<<one.two.txt
two
귀하의 경우 sed
지원하지 않습니다 -r
, 단지의 경우 교체 ()
와 \(
와를 \)
한 다음 -r
필요하지 않습니다.
답변
perl을 사용할 수 있다면 다른 솔루션보다 더 읽기 /x
쉽습니다. 특히 정규 표현식 언어가 더 표현력 있고 수정자가있어 명확한 정규 표현식을 작성할 수 있기 때문입니다.
perl -e 'print $1 if shift =~ m{ ( [^/]{3} ) [.] [^./]* \z }x' -- "$file"
일치하는 항목이 없으면 (기본 이름에 확장자가 없거나 확장자 앞의 루트가 너무 짧은 경우) 아무것도 인쇄하지 않습니다. 요구 사항에 따라 정규식을 조정할 수 있습니다. 이 정규식은 다음과 같은 제약 조건을 시행합니다.
- 최종 확장 앞의 3 자 (마지막 점 다음 부분 포함)와 일치합니다. 이 3 개의 문자는 점을 포함 할 수 있습니다.
- 확장명은 비워 둘 수 있습니다 (점 제외).
- 일치하는 부분과 확장명은 기본 이름 (마지막 슬래시 다음 부분)의 일부 여야합니다.
이것을 명령 대체에 사용하면 후행 줄 바꿈을 너무 많이 제거하는 데 문제가 있으며, 이는 스테판의 대답에도 영향을 미칩니다. 두 경우 모두 다룰 수 있지만 여기에서 조금 더 쉽습니다.
lastpart=$(
perl -e 'print "$1x" if shift =~ m{ ( [^/]{3} ) [.] [^./]* \z }x' -- "$file"
)
lastpart=${lastpart%x} # allow for possible trailing newline
답변
파이썬 2.7
$ echo /path/to/somefile.txt | python -c "import sys, os; print '.'.join(os.path.basename(sys.stdin.read()).split('.')[:-1])[-3:]"
ile
$ echo file.one.two.three | python -c "import sys, os; print '.'.join(os.path.basename(sys.stdin.read()).split('.')[:-1])[-3:]"
two
답변
나는이 bash 함수 pathStr ()이 당신이 찾고있는 것을 할 것이라고 생각합니다.
awk, sed, grep, perl 또는 expr이 필요하지 않습니다. 그것은 bash 내장만을 사용하므로 매우 빠릅니다.
또한 종속 argsNumber 및 isOption 함수를 포함했지만 해당 기능을 pathStr에 쉽게 통합 할 수 있습니다.
종속 함수 ifHelpShow는 터미널 명령 행 또는 YAD 를 통해 GUI 대화 상자에 도움말 텍스트를 출력하기위한 많은 하위 종속성이 있으므로 포함되지 않습니다 . 전달 된 도움말 텍스트는 설명서 용으로 포함되어 있습니다. ifHelpShow 및 해당 종속 항목을 원하는 경우 조언하십시오.
function pathStr () {
ifHelpShow "$1" 'pathStr --OPTION FILENAME
Given FILENAME, pathStr echos the segment chosen by --OPTION of the
"absolute-logical" pathname. Only one segment can be retrieved at a time and
only the FILENAME string is parsed. The filesystem is never accessed, except
to get the current directory in order to build an absolute path from a relative
path. Thus, this function may be used on a FILENAME that does not yet exist.
Path characteristics:
File paths are "absolute" or "relative", and "logical" or "physical".
If current directory is "/root", then for "bashtool" in the "sbin" subdirectory ...
Absolute path: /root/sbin/bashtool
Relative path: sbin/bashtool
If "/root/sbin" is a symlink to "/initrd/mnt/dev_save/share/sbin", then ...
Logical path: /root/sbin/bashtool
Physical path: /initrd/mnt/dev_save/share/sbin/bashtool
(aka: the "canonical" path)
Options:
--path Absolute-logical path including filename with extension(s)
~/sbin/file.name.ext: /root/sbin/file.name.ext
--dir Absolute-logical path of directory containing FILENAME (which can be a directory).
~/sbin/file.name.ext: /root/sbin
--file Filename only, including extension(s).
~/sbin/file.name.ext: file.name.ext
--base Filename only, up to last dot(.).
~/sbin/file.name.ext: file.name
--ext Filename after last dot(.).
~/sbin/file.name.ext: ext
Todo:
Optimize by using a regex to match --options so getting argument only done once.
Revised:
20131231 docsalvage' && return
#
local _option="$1"
local _optarg="$2"
local _cwd="$(pwd)"
local _fullpath=
local _tmp1=
local _tmp2=
#
# validate there are 2 args and first is an --option
[[ $(argsNumber "$@") != 2 ]] && return 1
! isOption "$@" && return 1
#
# determine full path of _optarg given
if [[ ${_optarg:0:1} == "/" ]]
then
_fullpath="$_optarg"
else
_fullpath="$_cwd/$_optarg"
fi
#
case "$_option" in
--path) echo "$_fullpath" ; return 0;;
--dir) echo "${_fullpath%/*}" ; return 0;;
--file) echo "${_fullpath##*/}" ; return 0;;
--base) _tmp1="${_fullpath##*/}"; echo "${_tmp1%.*}" ; return 0;;
--ext) _tmp1="${_fullpath##*/}";
_tmp2="${_tmp1##*.}";
[[ "$_tmp2" != "$_tmp1" ]] && { echo "$_tmp2"; }
return 0;;
esac
return 1
}
function argsNumber () {
ifHelpShow "$1" 'argsNumber "$@"
Echos number of arguments.
Wrapper for "$#" or "${#@}" which are equivalent.
Verified by testing on bash 4.1.0(1):
20140627 docsalvage
Replaces:
argsCount
Revised:
20140627 docsalvage' && return
#
echo "$#"
return 0
}
function isOption () {
# isOption "$@"
# Return true (0) if argument has 1 or more leading hyphens.
# Example:
# isOption "$@" && ...
# Note:
# Cannot use ifHelpShow() here since cannot distinguish 'isOption --help'
# from 'isOption "$@"' where first argument in "$@" is '--help'
# Revised:
# 20140117 docsalvage
#
# support both short and long options
[[ "${1:0:1}" == "-" ]] && return 0
return 1
}
자원