Por que uma função de movimentação/cópia de arquivo move apenas um arquivo por vez ao usar o curinga “*”?

Por que uma função de movimentação/cópia de arquivo move apenas um arquivo por vez ao usar o curinga “*”?
function mv1 { mv -n "$1" "targetdir" -v |wc -l ;}
mv1 *.png

Ele move apenas o primeiro .pngarquivo que encontra, não todos eles.

Como posso aplicar o comando a todos os arquivos que correspondem aos curingas?

Responder1

mv1 *.pngprimeiro expande o padrão curinga *.pngna lista de nomes de arquivos correspondentes e, em seguida, passa essa lista de nomes de arquivos para a função.

Então, dentro da função $1significa: pegar o primeiro argumento da função, dividi-lo onde ele contém espaços em branco e substituir qualquer uma das partes separadas por espaços em branco que contenham caracteres curinga e correspondam a pelo menos um nome de arquivo pela lista de nomes de arquivos correspondentes. Parece complicado? É, e esse comportamento só é útil ocasionalmente e costuma ser problemático. Esse comportamento de divisão e correspondência ocorre apenas se $1ocorrer fora das aspas duplas, portanto a correção é fácil: use aspas duplas.Sempre coloque aspas duplas nas substituições de variáveisa menos que você tenha um bom motivo para não fazê-lo.

Por exemplo, se o diretório atual contém os dois arquivos A* algorithm.pnge graph1.png, então mv1 *.pngpassa A* algorithm.pngcomo o primeiro argumento para a função e graph1.pngcomo o segundo argumento. Então $1é dividido em A*e algorithm.png. O padrão A*corresponde A* algorithm.pnge algorithm.pngnão contém caracteres curinga. Então a função acaba rodando mvcom os argumentos -n, A* algorithm.png, algorithm.png, targetdire -v. Se você corrigir a função para

function mv1 { mv -n "$1" "targetdir" -v |wc -l ;}

então ele moverá corretamente o primeiro arquivo.

Para processartodosos argumentos, diga ao shell para processar todos os argumentos e não apenas o primeiro. Você pode usar "$@"para significar a lista completa de argumentos passados ​​para a função.

function mv1 { mv -n "$@" "targetdir" -v |wc -l ;}

Isso está quase correto, mas ainda falhará se um nome de arquivo começar com o caractere -, porque mvtratará esse argumento como uma opção. Passe --para mvpara dizer “não há mais opções após este ponto”. Esta é uma convenção muito comum que a maioria dos comandos suporta.

function mv1 { mv -n -v -- "$@" "targetdir" |wc -l ;}

Um problema remanescente é que, se mvfalhar, esta função retornará um status de sucesso, porque o status de saída dos comandos no lado esquerdo de um canal é ignorado. No bash (ou ksh), você pode usar set -o pipefailpara fazer o pipeline falhar. Observe que definir esta opção pode causar falha em outro código executado no mesmo shell, portanto, você deve configurá-la localmente na função, o que é possível desde o bash 4.4.

function mv1 {
  local -
  set -o pipefail
  mv -n -v -- "$@" "targetdir" | wc -l
}

Nas versões anteriores, a configuração pipefailseria frágil, então seria melhor verificar PIPESTATUSexplicitamente.

function mv1 {
  mv -n -v -- "$@" "targetdir" | wc -l
  ((!${PIPESTATUS[0] && !${PIPESTATUS[1]}}))
}

Responder2

$1é o primeiro argumento da função, aqui o primeiro arquivo que corresponde a *.png. Eu acho que "$@"é isso que você deseja usar em vez de $1.

Responder3

Você teria que usar mv1 \*.png.

Ao interagir com funções, o Terminal Linux não passa o asterisco diretamente para o comando, mas seleciona o primeiro parâmetro correspondente e passa-o para o comando.

Para permitir que o asterisco passe diretamente, é necessário escapar do asterisco usando uma barra invertida.

informação relacionada