Obter o nome do diretório atual (sem o caminho completo) em um script Bash

820

Como obteria apenas o nome do diretório de trabalho atual em um script bash ou, melhor ainda, apenas um comando de terminal.

pwdfornece o caminho completo do diretório de trabalho atual, por exemplo, /opt/local/binmas eu só querobin

Derek Dahmer
fonte
1
Com o caminho completo, consulte: Obtendo o diretório de origem de um script Bash de dentro .
Kenorb 19/07

Respostas:

1116

Não há necessidade de nome de base e, especialmente, não é necessário um subshell executando o pwd (que adiciona uma operação de garfo extra e cara ); o shell pode fazer isso internamente usando a expansão de parâmetros :

result=${PWD##*/}          # to assign to a variable

printf '%s\n' "${PWD##*/}" # to print to stdout
                           # ...more robust than echo for unusual names
                           #    (consider a directory named -e or -n)

printf '%q\n' "${PWD##*/}" # to print to stdout, quoted for use as shell input
                           # ...useful to make hidden characters readable.

Observe que, se você estiver aplicando essa técnica em outras circunstâncias (não PWD, mas em alguma outra variável com um nome de diretório), poderá ser necessário aparar as barras finais. A seguir, use o suporte extglob do bash para funcionar mesmo com várias barras finais:

dirname=/path/to/somewhere//
shopt -s extglob           # enable +(...) glob syntax
result=${dirname%%+(/)}    # trim however many trailing slashes exist
result=${result##*/}       # remove everything before the last / that still remains
printf '%s\n' "$result"

Como alternativa, sem extglob:

dirname="/path/to/somewhere//"
result="${dirname%"${dirname##*[!/]}"}" # extglob-free multi-trailing-/ trim
result="${result##*/}"                  # remove everything before the last /
Charles Duffy
fonte
31
Qual é a diferença entre $ {PWD ## * /} e $ PWD?
Mr_Chimp
24
O @Mr_Chimp the first é uma operação de expansão de parâmetro que apara o restante do diretório para fornecer apenas o nome da base. Eu tenho um link na minha resposta para a documentação sobre expansão de parâmetros; sinta-se à vontade para acompanhar isso se tiver alguma dúvida.
Charles Duffy
24
@stefgosselin $PWDé o nome de uma variável bash interna ; todos os nomes de variáveis ​​integrados estão em maiúsculas (para diferenciá-los das variáveis ​​locais, que sempre devem ter pelo menos um caractere minúsculo). result=${PWD#*/}faz não avaliar a /full/path/to/directory; em vez disso, retira apenas o primeiro elemento, tornando-o path/to/directory; o uso de dois #caracteres faz com que o padrão seja ganancioso, correspondendo ao máximo de caracteres possível. Leia a página de expansão de parâmetros, vinculada na resposta, para mais informações.
precisa
5
Observe que a saída de pwdnem sempre é igual ao valor de $PWD, devido a complicações decorrentes de cdlinks simbólicos, se o bash tem a -o physicalopção definida e assim por diante. Isso costumava ficar especialmente desagradável ao lidar com diretórios montados automaticamente, onde a gravação do caminho físico em vez do lógico produziria um caminho que, se usado, permitiria ao montador desmontar espontaneamente o diretório que estava usando.
Alex North-Keys
3
Agradável! Alguém deveria escrever um livro para velhos caras do Borne como eu. Talvez haja um por aí? Poderia ter um crotchity sys velhos admin dizendo coisas como, "de volta no meu dia apenas she cshe se você queria a tecla de retrocesso para o trabalho que você tinha que ler toda sttypágina do homem, e nós gostamos!"
Red Cricket
335

Use o basenameprograma. Para o seu caso:

% basename "$PWD"
bin
Arkady
fonte
20
Não é esse o objetivo do basenameprograma? O que há de errado com essa resposta, além de perder as aspas?
Nacht - Restabelece Monica
11
@Nacht basenameé de fato um programa externo que faz a coisa certa, mas a execução de qualquer programa externo para uma festança coisa pode fazer out-of-the-box usando apenas funcionalidade interna é bobagem, incorrendo impacto no desempenho ( fork(), execve(), wait(), etc) para sem motivo.
Charles Duffy
3
... como um aparte, minhas objeções basenameaqui não se aplicam dirname, porque dirnametem uma funcionalidade que ${PWD##*/}não - transformar uma string sem barras para ., por exemplo. Portanto, embora o uso dirnamecomo ferramenta externa tenha sobrecarga de desempenho, também possui funcionalidade que ajuda a compensar a mesma.
Charles Duffy
4
@LouisMaddox, um pouco desconcertante: a functionpalavra-chave é incompatível com POSIX sem adicionar nenhum valor à sintaxe padronizada, e a falta de aspas criaria erros nos nomes de diretórios com espaços. wdexec() { "./$(basename "$PWD")"; }pode ser uma alternativa preferível.
Charles Duffy
28
Claro que usar basenameé menos "eficiente". Mas é provavelmente mais eficiente em termos de produtividade do desenvolvedor, porque é fácil de lembrar em comparação com a feia sintaxe do bash. Então, se você se lembra, vá em frente. Caso contrário, basenamefunciona tão bem. Alguém já teve que melhorar o "desempenho" de um script bash e torná-lo mais eficiente?
usr
139
$ echo "${PWD##*/}"

O que outras pessoas estão dizendo

DigitalRoss
fonte
2
@jocap ... exceto que o uso de eco lá, sem citar o argumento, está errado . Se o nome do diretório tiver vários espaços ou um caractere de tabulação, ele será recolhido em um único espaço; se tiver um caractere curinga, será expandido. O uso correto seria echo "${PWD##*/}".
Charles Duffy
9
@jocap ... também, não tenho certeza de que "eco" seja de fato uma parte legítima da resposta. A questão era como obter a resposta, não como imprimir a resposta; se o objetivo era atribuí-lo a uma variável, por exemplo, name=${PWD##*/}estaria certo e name=$(echo "${PWD##*/}")errado (no sentido de ineficiência desnecessária).
Charles Duffy
24

Você pode usar uma combinação de pwd e basename. Por exemplo

#!/bin/bash

CURRENT=`pwd`
BASENAME=`basename "$CURRENT"`

echo "$BASENAME"

exit;
mbelos
fonte
18
Por favor não. Os backticks criam um subshell (portanto, uma operação de fork) - e, neste caso, você os está usando duas vezes ! [Como uma queixa adicional, ainda que estilística - os nomes das variáveis ​​devem estar em letras minúsculas, a menos que você os exporte para o ambiente ou se for um caso reservado especial].
Charles Duffy
11
e, como segundo estilo, os backtics de quibble devem ser substituídos por $ (). garfos ainda mais legíveis e com menos excaping necessário.
Jeremy Wall
1
Por convenção, as variáveis ​​de ambiente (PATH, EDITOR, SHELL, ...) e variáveis ​​internas do shell (BASH_VERSION, RANDOM, ...) são totalmente capitalizadas. Todos os outros nomes de variáveis ​​devem estar em minúsculas. Como os nomes das variáveis ​​diferenciam maiúsculas de minúsculas, essa convenção evita a substituição acidental de variáveis ​​ambientais e internas.
Rany Albeg Wein
2
Depende da convenção. Pode-se argumentar que todas as variáveis ​​do shell são variáveis ​​de ambiente (dependendo do shell) e, portanto, todas as variáveis ​​do shell devem estar em maiúsculas. Alguém poderia argumentar com base no escopo - variáveis ​​globais são maiúsculas, enquanto variáveis ​​locais são minúsculas e todas são globais. Ainda outro pode argumentar que a criação de variáveis ​​em maiúsculas adiciona uma camada extra de contraste com os comandos que cobrem os scripts do shell, que também são palavras curtas, mas significativas, em minúsculas. Posso ou não concordar / com qualquer um desses argumentos, mas vi os três em uso. :)
dannysauer
17

Que tal grep:

pwd | grep -o '[^/]*$'
laranja
fonte
não recomendaria porque parece para obter alguma informação de cor enquanto pwd | xargs basenamenão .. provavelmente não tão importante, mas a outra resposta é mais simples e mais consistente em ambientes
davidhq
8

Gosto da resposta selecionada (Charles Duffy), mas tenha cuidado se você estiver em um diretório com link simbólico e quiser o nome do diretório de destino. Infelizmente, não acho que isso possa ser feito em uma expressão de expansão de parâmetro único, talvez eu esteja enganado. Isso deve funcionar:

target_PWD=$(readlink -f .)
echo ${target_PWD##*/}

Para ver isso, um experimento:

cd foo
ln -s . bar
echo ${PWD##*/}

relatórios "barra"

DIRNAME

Para mostrar os principais diretórios de um caminho (sem incorrer em um fork-exec de / usr / bin / dirname):

echo ${target_PWD%/*}

Isto irá, por exemplo, transformar foo / bar / baz -> foo / bar

FDS
fonte
5
Infelizmente, readlink -fé uma extensão GNU e, portanto, não está disponível nos BSDs (incluindo o OS X).
Xiong Chiamiov 26/10/2013
8
echo "$PWD" | sed 's!.*/!!'

Se você estiver usando o Bourne shell ou ${PWD##*/}não estiver disponível.

anômalo
fonte
4
Para sua informação, ${PWD##*/}é o POSIX sh - todo moderno / bin / sh (incluindo dash, ash, etc) suporta; para atingir o Bourne real em uma caixa recente, você precisa estar em um sistema Solaris levemente antigo. Além disso - echo "$PWD"; deixar de fora as aspas leva a erros (se o nome do diretório tiver espaços, caracteres curinga, etc.).
Charles Duffy
1
Sim, eu estava usando um sistema Solaris antigo. Eu atualizei o comando para usar aspas.
Anomal
7

Esta discussão é ótima! Aqui está mais um sabor:

pwd | awk -F / '{print $NF}'
rodvlopes
fonte
5

Surpreendentemente, ninguém mencionou essa alternativa que usa apenas comandos bash internos:

i="$IFS";IFS='/';set -f;p=($PWD);set +f;IFS="$i";echo "${p[-1]}"

Como um bônus adicional, você pode facilmente obter o nome do diretório pai com:

[ "${#p[@]}" -gt 1 ] && echo "${p[-2]}"

Eles funcionarão no Bash 4.3-alpha ou mais recente.

Arton Dorneles
fonte
surpreendentemente? apenas brincando, mas outras são muito mais curto e fácil de lembrar
codewandler
Isso é bem engraçado = P Não tinha ouvido falar em $ IFS (separador interno de campos) antes disso. minha festa era muito velho, mas pode testá-lo aqui: jdoodle.com/test-bash-shell-script-online
Stan Kurdziel
5

Usar:

basename "$PWD"

OU

IFS=/ 
var=($PWD)
echo ${var[-1]} 

Volte o IFS (Internal Filename Separator) de volta ao espaço.

IFS= 

Há um espaço após o IFS.

Fogo no céu
fonte
4
basename $(pwd)

ou

echo "$(basename $(pwd))"
user3278975
fonte
2
Precisa de mais aspas para estar correto - como é, a saída de pwdé dividida em cadeia e expandida antes de ser passada para basename.
Charles Duffy
Assim, basename "$(pwd)"- apesar de que é muito ineficiente em comparação com apenas basename "$PWD", que é em si ineficiente em comparação ao uso de uma expansão de parâmetros em vez de chamar basenamea todos.
Charles Duffy
4

Para os jóqueis por aí como eu:

find $PWD -maxdepth 0 -printf "%f\n"
user2208522
fonte
Altere $PWDpara "$PWD"para manipular corretamente nomes de diretório incomuns.
Charles Duffy
3

Eu costumo usar isso em scripts sh

SCRIPTSRC=`readlink -f "$0" || echo "$0"`
RUN_PATH=`dirname "${SCRIPTSRC}" || echo .`
echo "Running from ${RUN_PATH}"
...
cd ${RUN_PATH}/subfolder

você pode usar isso para automatizar as coisas ...

Glória holandesa
fonte
readlink: illegal option -- f usage: readlink [-n] [file ...]
2

Abaixo grep com regex também está funcionando,

>pwd | grep -o "\w*-*$"
Abhishek Gurjar
fonte
3
Não funciona se o nome do diretório contiver caracteres "-".
Daniula
1

Apenas use:

pwd | xargs basename

ou

basename "`pwd`"
marcos
fonte
2
Essa é, em todos os aspectos, uma versão pior da resposta fornecida anteriormente por Arkady ( stackoverflow.com/a/1371267/14122 ): A xargsformulação é ineficiente e com erros quando os nomes de diretório contêm linhas de nova linha literais ou caracteres de aspas, e a segunda formulação chama o pwdcomando em uma subshell, em vez de recuperar o mesmo resultado por meio de uma expansão variável interna.
Charles Duffy
@CharlesDuffy Não obstante, é uma resposta válida e prática à pergunta.
bachph 14/03
1

Se você quiser ver apenas o diretório atual na região do prompt do bash, poderá editar o .bashrcarquivo ~. Mude \wpara \Wna linha:

PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

Execute source ~/.bashrce ele exibirá apenas o nome do diretório na região do prompt.

Ref: /superuser/60555/show-only-current-directory-name-not-full-path-on-bash-prompt

Gautam Sreekumar
fonte
0

Você pode usar o utilitário basename, que exclui qualquer prefixo que termine em / e o sufixo (se presente na string) da string e imprime o resultado na saída padrão.

$basename <path-of-directory>
niks_4143
fonte
0

Eu prefiro usar gbasename, que faz parte do GNU coreutils.

Steve Benner
fonte
Para sua informação, ele não vem com minha distribuição do Ubuntu.
Katastic Voyage
@ KatasticVoyage, está lá no Ubuntu, é chamado apenas basenamelá. Normalmente, ele é chamado apenas gbasenameno MacOS e em outras plataformas que são fornecidas com um nome de base não-GNU.
Charles Duffy
-1

Os comandos a seguir resultarão na impressão do diretório de trabalho atual em um script bash.

pushd .
CURRENT_DIR="`cd $1; pwd`"
popd
echo $CURRENT_DIR
Paul Sabou
fonte
2
(1) Não sei ao certo onde estamos garantindo que a lista de argumentos seja tal que $1contenha o diretório de trabalho atual. (2) O pushde popdnão serve para nada aqui, porque qualquer coisa dentro dos backticks é feita em um subshell - portanto, não pode afetar o diretório do shell pai para começar. (3) O uso "$(cd "$1"; pwd)"seria mais legível e flexível em relação aos nomes de diretório com espaço em branco.
Charles Duffy