Obter o último campo usando substr do awk


99

Estou tentando usar awkpara obter o nome de um arquivo, dado o caminho absoluto para o arquivo.
Por exemplo, ao receber o caminho de entrada /home/parent/child/filenameque gostaria de obter filename , tentei:

awk -F "/" '{print $5}' input

que funciona perfeitamente.
No entanto, estou codificando, o $5que seria incorreto se minha entrada tivesse a seguinte estrutura:

/home/parent/child1/child2/filename

Portanto, uma solução genérica exige sempre pegar o último campo (que será o nome do arquivo).

Existe uma maneira simples de fazer isso com a função substr do awk?


2
como alguém apontou, usar basenameé a forma oficial de fazer isso, usar awkpara isso não é bom para dizer o mínimo. : D
Kashyap

Respostas:


206

Use o fato de que awkdivide as linhas em campos com base em um separador de campo, que você pode definir. Portanto, definindo o separador de campo para /você pode dizer:

awk -F "/" '{print $NF}' input

como NFse refere ao número de campos do registro atual, impressão$NF significa imprimir o último.

Então, dado um arquivo como este:

/home/parent/child1/child2/child3/filename
/home/parent/child1/child2/filename
/home/parent/child1/filename

Este seria o resultado:

$ awk -F"/" '{print $NF}' file
filename
filename
filename

3
Funciona perfeitamente. Não pensei na variável $ NF. Obrigado pela sua resposta imediata simples e eficaz.
Mari

6
Para o penúltimo, use $(NF-1).
Itachi

1
Estava escrevendo um script para trabalhar com alguns comandos do docker e isso funcionou. Obrigado!
Harlin de

30

Nesse caso, é melhor usar em basenamevez de awk:

 $ basename /home/parent/child1/child2/filename
 filename

4
@Mari FYI dirname faz o oposto e remove o arquivo, não o caminho.
Chris Seymour

5

Outra opção é usar a bash substituição de parâmetro .

$ foo="/home/parent/child/filename"
$ echo ${foo##*/}
filename
$ foo="/home/parent/child/child2/filename"
$ echo ${foo##*/}
filename

5

Se você está aberto a uma solução Perl, aqui está uma semelhante à solução awk de Fedorqui:

perl -F/ -lane 'print $F[-1]' input

-F/especifica /como o separador de campo
$F[-1]é o último elemento na @Fmatriz de divisão automática


1
Obrigado. Isso me ajudou a conseguir o último, mas um campo. perl -F "/" -lane 'print $ F [-2]' input
riderchap

isso funcionou para mim ao receber de entrada canalizada. Esqueci totalmente o perl.
Christopher

3

Deve ser um comentário para a resposta do nome de base, mas não tenho motivos suficientes.

Se você não usar aspas duplas, basenamenão funcionará com o caminho onde há caractere de espaço:

$ basename /home/foo/bar foo/bar.png
bar

ok com aspas ""

$ basename "/home/foo/bar foo/bar.png"
bar.png

exemplo de arquivo

$ cat a
/home/parent/child 1/child 2/child 3/filename1
/home/parent/child 1/child2/filename2
/home/parent/child1/filename3

$ while read b ; do basename "$b" ; done < a
filename1
filename2
filename3

3

Eu sei que estou cerca de 3 anos atrasado nisso, mas .... você deve considerar a expansão dos parâmetros, é embutida e mais rápida.

se sua entrada estiver em uma var, digamos, $ var1, basta fazer ${var1##*/}. Olhe abaixo

$ var1='/home/parent/child1/filename'
$ echo ${var1##*/}
filename
$ var1='/home/parent/child1/child2/filename'
$ echo ${var1##*/}
filename
$ var1='/home/parent/child1/child2/child3/filename'
$ echo ${var1##*/}
filename

2

Tipo 5 anos atrasado, eu sei, obrigado por todas as propostas, eu costumava fazer isso da seguinte maneira:

$ echo /home/parent/child1/child2/filename | rev | cut -d '/' -f1 | rev
filename

Fico feliz em notar que existem maneiras melhores


-1

Você também pode usar:

    sed -n 's/.*\/\([^\/]\{1,\}\)$/\1/p'

ou

    sed -n 's/.*\/\([^\/]*\)$/\1/p'

1
Neste ponto, ninguém votou a favor desta resposta, provavelmente porque é comparativamente feia :)
Josip Rodin
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.