Script para trocar os nomes de dois arquivos

7

Eu sou novo no script Bash. Eu tenho tentado fazer um script que irá trocar os nomes de arquivos dos dois arquivos passados para ele pelo usuário.

Aqui está uma foto das duas versões do meu script até agora

Aqui está o script em formato de texto com mv :

#! /bin/bash
file1=$file1
file2=$file2

echo "write file name :"
read file1 file2

if [[ $file1 = $file2 ]]
then 
  cp $file1 $file1
  mv $file1 $file2 
fi

if [[ $file2 = $file1 ]]
then   
  mv $file2 $file1
fi

Mas minha pergunta é se eu posso fazer um script que deixe o usuário escrever 2 nomes de arquivos primeiro, então o script irá trocar os 2 nomes de arquivos

O básico de trocar nomes de arquivos que eu li é isto

cp $file1 temporaryfile
mv $file1 $file2
mv $file2 temporyfile
    
por DeadPool69 05.12.2017 / 07:32

2 respostas

11

Uma maneira possível de fazer isso

Um tempo atrás, criei uma função especificamente para esse propósito, que guardo no meu .bashrc , e pode ser adaptada em um script. Você deve estar aproveitando os parâmetros posicionais para que os usuários possam colocar nomes de arquivos na linha de comando. Aqui está a minha função original:

swap_files() {
    if [ $# -ne 2 ]
    then
        echo "Usage: swap_files file1 file2"
    else
        local TMPFILE=$(mktemp) 
        mv -- "" "$TMPFILE"
        mv -- "" ""
        mv -- "$TMPFILE" ""
    fi
}

Você pode se livrar da declaração swap_files(){ , local keyword e fechar } e transformá-la em um script - basta adicionar #!/bin/bash na parte superior. Concedido há toneladas de coisas que podem ser melhoradas, mas no nível muito básico que é quase tão simples quanto o swapping (que por sinal é freqüentemente ensinado em C para trocar itens de array, mas isso é apenas um tópico tangente).

#!/bin/bash
if [ $# -ne 2 ]
then
    printf "Usage: swap_files file1 file2\n" > /dev/stderr
else
    TMPFILE=$(mktemp)
    mv -- "" "$TMPFILE"
    mv -- "" ""
    mv -- "$TMPFILE" ""
fi

Claro, lembre-se de citar os parâmetros posicionais se os nomes dos arquivos contiverem espaços. Assim:

swap_files 'file 1' 'file 2'

Anote o uso de -- para evitar problemas com nomes de arquivos que possuem - neles. Uma maneira melhor seria adotar o hábito de referenciar arquivos no diretório de trabalho atual com ./ , especialmente se você estiver usando globstar * (globstar não é relevante nesta questão, mas vale a pena mencionar se estamos falando de nomes de arquivos com os principais - ). Além disso, ./ way é mais portável, já que algumas versões de mv , como , FreeBSD não tem a opção -- .

Como sugerido por terdon nos comentários, também podemos criar um arquivo temporário na pasta principal do primeiro arquivo para evitar a movimentação de arquivos nos sistemas de arquivos.

#!/bin/bash
if [ $# -ne 2 ]
then
    printf "Usage: swap_files file1 file2\n" > /dev/stderr
else
    file1_dir=${1%/*}
    # just in case there were no slashes removed, assume cwd
    if [ "$file1_dir" = "" ]; then
        file1_dir="."
    fi
    tmpfile=$(mktemp -p "$file1_dir" )
    mv -- "" "$tmpfile"
    mv -- "" ""
    mv -- "$tmpfile" ""
fi

Seu script e coisas para melhorar

1. Atribuição de variável redundante

file1=$file1
file2=$file2

Esta parte atribui a variável $file1 a ... file1 variable; há dois problemas com isso - atribuir variável a si mesmo é redundante e não existe para começar, não há nenhuma declaração dessa variável anteriormente no script.

2. Cuidado com divisão de palavras com leitura

Veja o que acontecerá se o usuário tentar colocar itens até mesmo entre aspas no comando read :

$ read file1 file2
'one potato' 'two potato'

$ echo "$file1"
'one

$ echo "$file2"
potato' 'two potato'

De acordo com o comportamento do shell, o shell divide tudo que é lido em stdin e tenta encaixar cada palavra nas variáveis correspondentes, e se as palavras excederem o número de variáveis - ele tenta empurrar tudo para a última variável. Eu recomendo que você leia em cada arquivo, um de cada vez.

3. Copiar para si mesmo é um erro

Você está fazendo

cp $file1 $file1;

Isso produzirá um erro

$ cp input.txt input.txt
cp: 'input.txt' and 'input.txt' are the same file

Talvez você quisesse fazer

cp "$file1" "$file1".tmp

Ou apenas use o comando mktemp como eu fiz. Observe também a citação de variáveis para evitar a divisão de palavras.

Outras maneiras divertidas de fazer isso

Você sabia que pode filtrar qualquer arquivo com o redirecionamento para fazer uma cópia? Portanto, usar mv ou cp não é o único caminho. Algo parecido com isto:

$ cat ./wallpaper.jpg > wallpaper.jpg.tmp
$ cat ./screenshot.jpg > wallpaper.jpg
$ cat ./wallpaper.jpg.tmp > ./screenshot.jpg
    
por Sergiy Kolodyazhnyy 05.12.2017 / 09:07
6

Você pode usar a expansão de parâmetro para a tarefa, se estiver obtendo seus dois nomes de arquivos, ou poderá lê-los no script. meu script de exemplo abaixo usa a expansão de parâmetro. E você pode querer usar um diretório temporário para suas opções de movimentação, porque se o nome do arquivo usado no script já existir, esse arquivo será sobrescrito silenciosamente.

#!/bin/bash

# creating a temporary directory to prevent overwrites if the file called 'tmpfile' exists
TMP=$(mktemp -d)

# copying the filenames into variables for later use
file1=""
file2=""

# swapping the files
# first move and rename file1 to $TMP/tempfile
mv "" $TMP/tempfile
# renaming file2 to file1 name
mv "" "$file1"
# now renaming and moving the tempfile to file2 
mv $TMP/tempfile "$file2"
# removing the temporary folder if tempfile is not existent anymore
[ -e $TMP/tempfile ] && echo "Error!" || rm -r $TMP

Em Bash-Manual # Shell-Parameters :

  

Um parâmetro é uma entidade que armazena valores. Pode ser um nome, um   número ou um dos caracteres especiais listados abaixo. Uma variável é um   parâmetro denotado por um nome. Uma variável tem um valor e zero ou mais   atributos. Atributos são atribuídos usando o comando declare builtin   (veja a descrição da declaração embutida no Bash Builtins).

     

Um parâmetro é definido se foi atribuído um valor. A string nula é   um valor válido. Quando uma variável é definida, ela pode ser desfeita apenas usando   o comando interno não definido.

     

Uma variável pode ser atribuída por uma declaração do formulário

     
    

nome = [ valor ]

  

E se você quiser ler os nomes dos arquivos em uma caixa de diálogo interativa:

#!/bin/bash

# creating a temporary directory to prevent overwrites if the file called 'tmpfile' exists
TMP=$(mktemp -d)

# reading the filenames from user input into variables for later use
read -rp "Enter first filename: " file1
read -rp "Enter second filename: " file2

# swapping the files
# first move and rename file1 to $TMP/tempfile
mv "$file1" $TMP/tempfile
# renaming file2 to file1 name
mv "$file2" "$file1"
# now renaming and moving the tempfile to file2 
mv $TMP/tempfile "$file2"
# removing the temporary folder if tempfile is not existent anymore
[ -e $TMP/tempfile ] && echo "Error!" || rm -r $TMP

Em Bash-Manual # Bash-Builtins :

  
read [-ers] [-a aname] [-d delim] [-i text] [-n nchars]
    [-N nchars] [-p prompt] [-t timeout] [-u fd] [name …]
     

Uma linha é lida da entrada padrão ou do descritor de arquivo fd   fornecido como um argumento para a opção -u , dividido em palavras conforme descrito acima em [Divisão do Word] [6], e a primeira palavra é atribuída ao primeiro nome , a segunda palavra para o segundo nome e assim por diante. Se houver mais palavras do que nomes, as palavras restantes e seus delimitadores intervenientes serão atribuídos ao último nome . Se houver menos palavras lidas do fluxo de entrada do que nomes, os nomes restantes receberão valores vazios. Os caracteres no valor da variável IFS são usados para dividir a linha em palavras usando as mesmas regras que o shell usa para expansão (descrito acima em [Divisão do Word] [6]). O caractere de barra invertida " \ " pode ser usado para remover qualquer significado especial para a próxima leitura de caractere e para continuação de linha. Se nenhum nome for fornecido, a linha lida será atribuída à variável REPLY .

read aceita várias opções. Nesse caso, dois são mais relevantes, já que você deseja fazer uma pergunta ao usuário e obter informações para eles. Essas opções são:

  
  • -r → Se essa opção for fornecida, a contrabarra não atuará como um caractere de escape. A barra invertida é considerada parte da linha. Em particular, um par de barra invertida-nova linha não pode ser usado como uma continuação de linha.
  •   
  • -p prompt → Exibe o prompt , sem uma nova linha, antes de tentar ler qualquer entrada. O prompt é exibido apenas se a entrada estiver vindo de um terminal.
  •   

Embora essa não seja a pior situação para esquecer -r , você quase sempre deseja incluí-la para impedir que \ atue como um caractere de escape. -p mostra ao usuário uma solicitação.

    
por Videonauth 05.12.2017 / 08:59