Como eu divido a variável $ 0 para encontrar o diretório e os caminhos relativos no bash?

10

A variável $ 0 contém as informações do caminho do script.

  • Como posso alterar as informações do caminho para caminho absoluto? Quero dizer como processar ~,., .. ou similar?
  • Como posso dividir as informações do caminho em diretório e nome de arquivo?

Eu poderia usar python / perl para isso, mas quero usar o bash, se possível.

prosseek
fonte
o que você está tentando alcançar? btw. chamar um script com ~ / foo.sh expandirá ~ para o meu diretório pessoal por US $ 0 com minha versão do bash (GNU bash, versão 4.1.5 (2) -release- (x86_64-unknown-linux-gnu))
echox

Respostas:

17

Você não precisa processar coisas como ~, o shell faz isso por você. É por isso que você pode passar ~/filenamepara qualquer script ou programa e ele funciona - todos esses programas não ~se autodenominam; seu shell converte o argumento para /home/username/filenamee passa para o programa:

$ echo ~/filename
/home/mrozekma/filename

Se você precisar de um nome de arquivo canônico (que não inclua coisas como ..), use realpath(obrigado Neil ):

$ realpath ~/../filename
/home/filename

Quanto à divisão do caminho no nome do diretório e no nome do arquivo, use dirnamee basename:

$ dirname /foo/bar/baz
/foo/bar

$ basename /foo/bar/baz
baz
Michael Mrozek
fonte
Eu acho que você quer dizer caminho real, não readlink. O readlink não funcionará na situação comum, ou seja, quando o link usar um caminho relativo e você não estiver no mesmo diretório que o link.
Neil Mayhew
@ Neil realpathé melhor, mas readlinkparece funcionar quando eu tento; você conhece um exemplo em que ele falha?
Michael Mrozek
1
cd /tmp; touch foo; ln -s foo bar; cd; readlink /tmp/bar. Isso retorna fooe realpathretorna /tmp/foo, que é o que você deseja.
Neil Mayhew
1
@ Neil, @ Michael: readlink -f(nota -f) é quase equivalente a realpath, e é mais portátil: readlink -fnos GNU coreutils e existe em alguns outros sistemas também; realpathé empacotado separadamente e pode não ser instalado (por exemplo, apenas 5 pacotes não tão comuns dependem dele no Ubuntu 10.04 ou Debian lenny).
Gilles 'SO- stop be evil'
@ Gilles: bom ponto. Obrigado pelo lembrete sobre o realpath -f.
Neil Mayhew
5

Usar dirname e basename como mencionado por Michael deve ser a maneira mais segura de obter o que você deseja.

De qualquer forma, se você realmente quiser fazer isso com "ferramentas apenas para o bash", poderá usar a substituição de parâmetro:

echo `basename $PWD`        # Basename of current working directory.
echo "${PWD##*/}"           # Basename of current working directory.
echo
echo `basename $0`          # Name of script.
echo $0                     # Name of script.
echo "${0##*/}"             # Name of script.
echo
filename=test.data
echo "${filename##*.}"      # data
                            # Extension of filename.

Este exemplo é retirado diretamente do Advanced Bash Scripting Guide, que vale uma olhada.

A explicação é bem simples:

$ {var # Pattern} Remova de $ var a parte mais curta de $ Pattern que corresponde ao front end de $ var. $ {var ## Pattern} Remova de $ var a parte mais longa de $ Pattern que corresponde ao front end de $ var.

Observe o padrão como algum regex e o #ou ##como algum tipo de modificador ganancioso / não ganancioso.

Isso pode se tornar útil se você precisar fazer algumas extrações mais complicadas de uma parte de caminhos.

echox
fonte
5
Cuidado ao usar ${...##...}amigos e não dirnamee basenameque eles não funcionam em todos os casos. Por exemplo, ambos ${0##*/}e ${0%/*}expanda para o mesmo que $0se $0não contenha a /.
Gilles 'SO- stop be evil'
@ Gilles eu entendo por que ${0%/*}não é uma boa alternativa para dirname. No entanto, o que há de errado em usar em ${0##*/}vez de basename?
toxalot
@toxalot Para este, o único problema é quando $0é um nome de diretório com um final /. Mas lamento meu conselho, porque dirnamenão melhora.
Gilles 'SO- stop being evil'
2

realpath é um comando que informa o caminho real (remove .. e links simbólicos etc.)

É padrão no FreeBSD. De acordo com esta discussão, também está disponível para Linux:

http://www.unix.com/shell-programming-scripting/89294-geting-real-path.html

Essa discussão também oferece uma solução bash:

$ bash -c "cd /foo/../bar/ ; pwd"

fonte
1
Exatamente, algo assim normalmente funciona: MYDIR = "$ (cd $ (dirname" $ ​​0 "); pwd)"
Kevin Cantu