Qual é a maneira mais simples de remover uma barra final de cada parâmetro?

91

Qual é a maneira mais simples de remover uma barra final de cada parâmetro no array '$ @', de modo que rsynccopie os diretórios por nome?

rsync -a --exclude='*~' "$@" "$dir"

O título foi alterado para esclarecimento. Para entender os comentários e responder sobre as várias barras finais, consulte o histórico de edição.

sid_com
fonte
2
possível duplicata de Remover barra do final de uma variável
Warren Dew

Respostas:

153

Você pode usar a ${parameter%word}expansão detalhada aqui . Aqui está um script de teste simples que demonstra o comportamento:

#!/bin/bash

# Call this as:
#   ./test.sh one/ two/ three/ 
#
# Output:
#  one two three

echo ${@%/}
Sean Bright
fonte
34
+1: Ser altamente pedante, isso removerá uma única barra, nem todas as barras finais. Para remover qualquer número de barras finais:shopt -s extglob; echo "${@%%+(/)}"
glenn jackman
24
Aviso: você pode não querer remover as barras finais em todos os casos. Se "/" for fornecido como argumento, a remoção da barra final terá ... consequências desastrosas.
Gordon Davisson
13
PROTIP: Combine tr -s /com regex variável para remover barras repetidas e, em seguida, remova a barra final. por exemploDIR=$(echo //some///badly/written///dir////// | tr -s /); DIR=${DIR%/}
Dave
1
Realmente? Em um prompt bash, execute set -- one///// two// three four/; shopt -s extglob; echo "${@%%+(/)}"e me diga o que você vê
glenn jackman
1
@twalberg Agradeço "protips" que não são apenas respostas alternativas para a pergunta do OP em threads de comentários.
Kyle Strand
36

A resposta aceita cortará UMA barra final.

Uma maneira de aparar várias barras finais é assim:

VALUE=/looks/like/a/path///

TRIMMED=$(echo $VALUE | sed 's:/*$::')

echo $VALUE $TRIMMED

Quais saídas:

/looks/like/a/path/// /looks/like/a/path
Chris Johnson
fonte
1
Não se esqueça de citar suas variáveis, caso contenham espaços em branco: TRIMMED=$(echo "$VALUE" | sed 's:/*$::')
tetsujin
1
Na verdade, isso não é necessário dentro da $()construção. No entanto, também é inofensivo :) portanto, é provavelmente uma boa prática usar aspas duplas "$VALUE"para não ter que decidir quando e quando não usá-las.
Chris Johnson
Há alguma maneira de combinar isso com uma captura anterior? Eu quero remover o protocolo e (se houver) barra final de um URL, mas echo "https://www.example.com/foo/" | sed -e 's|https*://\(.*\)/*$|\1|'não funciona (uma vez que o grupo de captura corresponde à barra final, eu acho). Posso fazer isso com dois comandos: echo "https://www.example.com/foo/" | sed -e 's|https*://\(.*\)$|\1|' -e 's|/*$||'mas gostaria de saber se poderia ser feito com um?
Adam
Essa resposta funcionou melhor para mim com um arquivo de entrada muito grande. Um simples sed 's:/*$::' < in.txt > out.txtfaz o trabalho em segundos
MitchellK
19

Isso funciona para mim: ${VAR%%+(/)}

Conforme descrito aqui http://wiki.bash-hackers.org/syntax/pattern

Pode ser necessário definir a opção extglob do shell. Não consigo ver que está habilitado para mim, mas ainda funciona

Ivan
fonte
2
Para consultar uma configuração: shopt extglobsem opções
glenn jackman
Este é o Extended Pattern Language e você deve definir extglob.
ingyhere
1
Isso não funciona no bash integrado do Mac OS X. A solução de Sean Bright acima faz:${VAR%/}
Alec Jacobson
12

realpathresolve determinado caminho. Entre outras coisas, ele também remove barras finais. Use -spara evitar seguir simlinks

DIR=/tmp/a///
echo $(realpath -s $DIR)
# output: /tmp/a
Czerny
fonte
1
Requer que todos os nós no caminho, exceto o último, exista. Se o usuário lançar algum caminho de não existência, realpathfalhará.
Tito Lívio
2
@Livy realpath --canonicalize-missingfunciona absolutamente corretamente com qualquer parte não existente do caminho
maoizm
7

Para sua informação, adicionei essas duas funções à minha com .bash_profilebase nas respostas encontradas no SO. Como disse Chris Johnson, todas as respostas usando ${x%/}remove apenas uma barra, essas funções farão o que dizem, espero que seja útil.

rem_trailing_slash() {
    echo "$1" | sed 's/\/*$//g'
}

force_trailing_slash() {
    echo "$(rem_trailing_slash "$1")/"
}
Jonathan H
fonte
4

No zsh, você pode usar o :amodificador.

export DIRECTORY='/some//path/name//'

echo "${DIRECTORY:a}"

=> /some/path/name

Isso funciona como, realpathmas não falha, com arquivos / diretórios ausentes como argumento.

Nicolai Fröhlich
fonte