기본 (접미사 빼기) 파일 이름의 마지막 3자를 추출하는 가장 짧은 방법 사용할 수있는 더 짧은 단일 명령이

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

결합 $IFSset팅 쉘 파라미터도 파싱 쉘 변수 통해 시추 매우 효과적인 수단이 될 수있다 :

(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"

일치하는 항목이 없으면 (기본 이름에 확장자가 없거나 확장자 앞의 루트가 너무 짧은 경우) 아무것도 인쇄하지 않습니다. 요구 사항에 따라 정규식을 조정할 수 있습니다. 이 정규식은 다음과 같은 제약 조건을 시행합니다.

  1. 최종 확장 앞의 3 자 (마지막 점 다음 부분 포함)와 일치합니다. 이 3 개의 문자는 점을 포함 할 수 있습니다.
  2. 확장명은 비워 둘 수 있습니다 (점 제외).
  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
}

자원