Comportamento estranho com o comando 'basename' do UNIX

2

Eu quero usar basename para obter os nomes básicos de uma lista delimitada por novas linhas de caminhos de arquivos. Parece funcionar quando n > 2:

$ basename 'echo -e '/foo/bar \n /food/baz \n /oof/rab''
bar
baz
rab

No entanto, quando n = 2, só gera a primeira linha.

$ basename 'echo -e '/foo/bar \n /food/baz''
bar

Por que não imprimiu baz ? Isso é um bug em basename ? Aqui está a minha informação de plataforma:

$ uname -a
Darwin MacBook-Pro-4.local 15.6.0 Darwin Kernel Version 15.6.0: Sun Jun  4 21:43:07 PDT 2017; root:xnu-3248.70.3~1/RELEASE_X86_64 x86_64
    
por Andy Carlson 08.09.2017 / 02:51

4 respostas

5

basename não pega uma lista de caminhos (sem falar de um delimitado por novas linhas), leva um único caminho (opcionalmente seguido por um sufixo para remover).

Além disso, quando você usa o comando (como echo ) em backticks, o shell pega a saída desse comando, divide em "palavras" separadas por espaço em branco (geralmente espaços, tabulações e novas linhas, mas você pode alterá-lo com $IFS ), expande todos os curingas encontrados e passa o resultado de tudo isso para o outro comando ( basename neste caso). Como resultado, as novas linhas na saída echo são tratadas como apenas mais espaços em branco entre as palavras, não passadas para basename . Então, este comando:

basename 'echo -e '/foo/bar \n /food/baz \n /oof/rab''

é equivalente a:

basename "/foo/bar" "/food/baz" "/oof/rab"

... e aparentemente quando basename obtém três argumentos como este, imprime o nome base de cada argumento separadamente. A página man do basename não documenta esse comportamento, então, se houver alguma coisa, é um erro não dar um erro nesse caso.

Da mesma forma, este comando:

basename 'echo -e '/foo/bar \n /food/baz''

é equivalente a

basename "/foo/bar" "/food/baz"

... mas, neste caso, o comportamento de basename é definido e é bem diferente - ele usa o nome base do primeiro argumento, remove o segundo argumento de seu final (se corresponder) e imprime o resultado. "bar" não termina com "/ food / baz", então apenas imprime "bar".

Além disso, echo -e é estranhamente não-portável (mesmo entre versões diferentes do OS X!). Abaixo, usei o formato de string $' ' do bash para obter as sequências de escape traduzidas.

Se você quiser imprimir os nomes base de uma lista separada por novas linhas de caminhos, use um loop para executar basename em cada um separadamente:

while read -r filepath; do
    basename "$filepath"
done <<< $'/foo/bar \n /food/baz'

Ou use apenas sed para excluir até o primeiro "/" em cada linha:

echo $'/foo/bar \n /food/baz \n /oof/rab' | sed 's@^.*/@@'
    
por 08.09.2017 / 08:19
2

basename tem um argumento opcional suffix por exemplo,

basename include/stdio.h .h

retorna "stdio"

Seu segundo argumento "/food/baz" delimitado por espaços em branco " \n " foi considerado como o sufixo. Como não é correspondido, devolve apenas "bar" .

Você pode tentar

basename 'echo -e '/foo/bar \n r''

que deve retornar "ba" .

    
por 08.09.2017 / 05:53
0

Não tenho certeza sobre as rotações do sistema operacional, mas pode haver alguns utilitários de nome de código diferentes. O meu (/ usr / bin / basename no Mac OS 10.12.6) dá um erro sobre esse "-e" no subcomando echo, mas isso pode ser meu shell (tcsh).

Isso funciona para mim:

basename /foo/bar /food/baz /oof/rab
    
por 08.09.2017 / 05:00
0

para complementar a resposta de @Gordon, basename (e seu amigo dirname ) é algum dia usado para remover sufixos.

$ basename /foo/bar.c .c
bar

isso pode ser útil ao compilar ou processar arquivos. por exemplo:

SRC=$1
DEST=$(dirname $SRC)/$(basename $SRC .txt).csv

process ${SRC} > ${DEST}

(note que o snippet acima não funcionará com o arquivo com char engraçado (espaço, tabulação ou nova linha)

    
por 29.09.2017 / 08:02