Как объединить модификаторы имен файлов в оболочке bash? - PullRequest
7 голосов
/ 28 февраля 2011

Я понимаю модификаторы # ##% %%, но не могу понять, возможно ли их связать вместе, как вы можете в tcsh.

Пример в tcsh

set f = /foo/bar/myfile.0076.jpg
echo $f:r:e
--> 0076

echo $f:h:t
--> bar

В bash я хотел бы знать, как сделать что-то вроде:

echo ${f%.*#*.}

в одну строку.

Моя цель - иметь возможность манипулировать именами файлов различными способами, как и когда это необходимо в командной строке. Я не пытаюсь написать сценарий для одного конкретного случая. Так что, если есть способ объединить эти модификаторы в цепочку или, может быть, есть другой способ, то я бы хотел знать. Спасибо

Ответы [ 3 ]

5 голосов
/ 28 февраля 2011

В bash вы можете вкладывать Расширения параметров , но только в части word в ${parameter#word}.

Например:

$ var="foo.bar.baz"; echo ${var%.*}
foo.bar
$ var="foo.bar.baz"; echo ${var#foo.bar}
.baz
$ var="foo.bar.baz"; echo ${var#${var%.*}}
.baz

Чтобы сделать то, что вы хотите, с простым расширением параметров, вам нужно иметь временную переменную, например:

$ var="/foo/bar/myfile.0076.jpg"; tmp=${var#*.}; out=${tmp%.*}; echo $out
0076

Однако, если вы хотите использовать встроенную функцию set, вы можете получить доступ ко всем полям за один раз, если использовать расширение расширения поиска / замены, например, так:

$ var="/foo/bar/myfile.0076.jpg"; set -- ${var//[.\/]/ }; echo $4
0076
2 голосов
/ 01 марта 2011

Один из способов добиться того, чего вы пытаетесь достичь, - использовать регулярные выражения Bash (версии 3.2 и новее).

f=/foo/bar/myfile.0076.jpg
pattern='/([^/]*)/[^/]*\.([0-9]*)\.'
[[ $f =~ $pattern ]]
echo ${BASH_REMATCH[1]}    # bar
echo ${BASH_REMATCH[2]}    # 0076

Вы можете применять операторы расширения фигурных скобок в последовательности:

f=/foo/bar/myfile.0076.jpg
r=${f%.*}  # remove the extension
r=${r#*.}  # remove the part before the first (now only) dot
echo $r    # 0076
r=${f%/*}  # similar, but use slash instead of dot
r=${r##*/}
echo $r    # bar

Другой способ - объединить расширение скобки с расширенными глобами:

shopt -s extglob
f=/foo/bar/myfile.0076.jpg
r=${f/%*([^0-9])}    # remove the non-digits from the beginning
r=${r/#*([^0-9])}    # remove the non-digits from the end
echo $r              # 0076
r=${f/#*(\/*([^\/])\/)}    # remove the first two slashes and what's between them
r=${r/%\/*(?)}             # remove the last slash and everything after it
echo $r                    # bar

Редактировать:

Вот мои функции Bash, которые выполняют basename иdirname.Они обрабатывают крайние случаи аналогично этим утилитам.

bn ()
{
    [[ $1 == / ]] && echo / || echo "${1##*/}"
}

dn ()
{
    [[ -z ${1%/*} ]] && echo / || {
        [[ $1 == .. ]] && echo . || echo "${1%/*}"
    }
}
1 голос
/ 02 марта 2011

Я нашел решение, которое очень близко подходит к простоте модификаторов имени файла tcsh.Я написал 4 функции и поместил их в .bashrc.

e() # the extension
E() # everything but the extension
t() # the tail - i.e. everything after the last /
T() # everything but the tail (head)

Определения в конце.

Эти функции могут принимать аргумент, например, так:

f=foo/bar/my_image_file.0076.jpg
e $f
--> jpg
E $f
--> foo/bar/my_image_file.0076

или принимать входные данные из канала, который является функциейиз tcsh, который я действительно хотел:

echo $f|E|e
--> 0076

или, конечно, комбинация:

T $f|t
--> bar

, и меня только что осенило, что он примет много файлов через канал:

ls foo/bar/
--> my_image_file.0075.jpg  my_image_file.0076.jpg
ls foo/bar/ |E|e
--> 0075
--> 0076

Определения:

#If there are no args, then assume input comes from a pipe.

function e(){
    if [ $# -ne 0 ]; then
        echo ${1##*.}  
    else
        while read data; do
            echo  ${data##*.}   ; 
        done
    fi
}

function E(){
    if [ $# -ne 0 ]; then
        echo ${1%.*} 
    else
        while read data; do
            echo ${data%.*}
        done
    fi
}

function t(){
    if [ $# -ne 0 ]; then
        echo ${1##*/}  
    else
        while read data; do
            echo  ${data##*/}   ; 
        done
    fi
}

function T(){
    if [ $# -ne 0 ]; then
        echo ${1%/*} 
    else
        while read data; do
            echo ${data%/*}
        done
    fi
}
...