Como comparar dois arquivos e, se encontrado, pedir ao usuário para excluir o arquivo duplicado usando o shell script?

2
Estou aprendendo Linux e foi dado este problema como minha lição de casa, mas eu não posso resolver isso que como podemos comparar dois arquivos de conteúdo no modo shell. (Aqui, podemos supor que ambos os arquivos com conteúdo de texto como este por exemplo. $ cat > f1 este é o arquivo 1)

$ cat duplicate_file.sh
echo "Enter file 1:"
read file1
echo "Enter file 2:"
read file2
cmp $file1 $file2 > newfile
x=' wc newfile | cut -d" " -f2 '
if [' $x -eq 0 ']
then
rm -i $file2
fi

Eu fiz este programa, mas isso não está funcionando !! Então, alguma sugestão ??

    
por Hariom kushwaha 10.11.2018 / 07:13

3 respostas

4

O problema imediato em seu código é o erro de sintaxe na leitura da linha

if [' $x -eq 0 ']

O [ e ] devem ser separados dos argumentos dentro de um caractere de espaço. Além disso, a substituição de comando nesta linha, '$x -eq 0' , não faz sentido, pois tentaria executar o valor de $x como um comando.

Você também tem problemas com a não-cotação de suas expansões de variáveis, o que desqualifica seu script de trabalhar em nomes de arquivos contendo caracteres em branco e padrões de globalização de nomes de arquivo.

O script também sobrecarrega incondicionalmente o arquivo newfile desnecessariamente (e falharia se newfile fosse o nome de um diretório existente) e não possui um #! -line.

Não faz sentido perguntar ao usuário interativamente por caminhos de arquivo. Seria melhor para o usuário poder fazer uso do preenchimento do nome do arquivo do shell na linha de comando e fornecer os nomes dos arquivos para os arquivos como dois operandos:

$ ./script.sh some/path/file1 some/other/path/file2

Se o script for executado dessa maneira, os dois nomes de caminho estarão disponíveis no script como "$1" e "$2" .

O utilitário cmp pode ser usado neste script sem criar um arquivo temporário . Em vez de redirecionar sua saída, deixe-a quieta usando sua opção -s (para "silencioso") e use seu status de saída para determinar se os dois arquivos eram idênticos ou não.

O script seria parecido com

#!/bin/sh

if cmp -s -- "$1" "$2"; then
    rm -i -- "$2"
fi

Ou mais curto,

#!/bin/sh

cmp -s -- "$1" "$2" && rm -i -- "$2"

Isso chamaria rm -i no segundo dos dois nomes de caminho se ele se referisse a um arquivo com conteúdo idêntico ao primeiro nome de caminho. O -- nos comandos cmp e rm é necessário para evitar interpretar um nome de arquivo começando com um traço como um conjunto de opções.

O problema com este script, como com o seu próprio script, é que se você der a ele o mesmo nome de caminho duas vezes , ou seja, comparar um arquivo com ele mesmo, oferecerá para removê-lo.

Portanto, também precisamos ter certeza de que os dois caminhos se referem a dois arquivos diferentes .

Você pode fazer isso comparando as duas strings de nome de caminho entre si:

#!/bin/sh

if [ "$1" != "$2" ] && cmp -s -- "$1" "$2"; then
    rm -i -- "$2"
fi

Isso pode ser suficiente para a maioria dos aplicativos, mas não leva em consideração links simbólicos. Em algumas shells você também pode usar o não-padrão -ef test ("arquivo igual") que testa se dois nomes de caminho se referem ao mesmo arquivo (mesmo número de nó e dispositivo):

#!/bin/bash

if ! [ "$1" -ef "$2" ] && cmp -s -- "$1" "$2"; then
    rm -i -- "$2"
fi

ou

#!/bin/bash

! [ "$1" -ef "$2" ] && cmp -s -- "$1" "$2" && rm -i -- "$2"

E com algumas verificações de integridade (também movendo o teste -ef para a seção de verificações de sanidade):

#!/bin/bash

if [ "$#" -ne 2 ]; then
    # did not get exactly two arguments
    printf 'Usage:\n\t%s file1 file2\n' "$0" >&2
    exit 1
elif [ ! -f "$1" ] || [ ! -f "$2" ]; then
    echo 'One of the files does not exist (or is not a regular file)' >&2
    exit 1
elif [ "$1" -ef "$2" ]; then
    printf '%s and %s refer to the same file\n' "$1" "$2" >&2
    exit 1
fi

cmp -s -- "$1" "$2" && rm -i -- "$2"

Note que citar as expansões de variáveis é importante, já que não é incomum que os nomes de caminho contenham espaços (no macOS, isso é muito comum). Duplicar as expansões de variáveis também os impede de serem interpretados como padrões de globalização de shell (seu código, por exemplo, não funciona em um arquivo chamado * ). Observe também o uso de uma linha #! apropriada para o script.

Se o seu dever de casa requer que você leia os nomes dos caminhos dos dois arquivos interativamente, faça isso com read -r e com IFS definido como uma string vazia. Isso permitiria que você lesse nomes de caminho começando com caracteres de espaço em branco e contendo \ caracteres:

#!/bin/bash

IFS= read -p '1st pathname: ' -r p1
IFS= read -p '2nd pathname: ' -r p2

if [ ! -f "$p1" ] || [ ! -f "$p2" ]; then
    echo 'One of the files does not exist (or is not a regular file)' >&2
    exit 1
elif [ "$p1" -ef "$p2" ]; then
    printf '%s and %s refer to the same file\n' "$p1" "$p2" >&2
    exit 1
fi

cmp -s -- "$p1" "$p2" && rm -i -- "$p2"

Relacionados:

Se em algum momento você precisar verificar se um arquivo está vazio, como em seu próprio código, não chame wc nele (ele é ineficiente, pois ele teria que ler o arquivo inteiro). Em vez disso, use um teste -s :

if [ -s "$pathname" ]; then
    printf '%s has non-zero size\n' "$pathname"
else
    printf '%s is empty (or does not exist)\n' "$pathname"
fi

Veja man test no seu sistema ou consulte o padrão POSIX para este utilitário .

    
por 10.11.2018 / 12:49
3

Primeiro, inclua shebang #! no topo, como #!/bin/bash

Você está tendo dois erros:

Em vez de

cmp $file1 $file2 > newfile,

deve ser

cmp -- "$file1" "$file2" > newfile

como esses valores dessas variáveis podem ter espaços, tabulações, nova linha (caracteres de $IFS ), * , [ , ? (caracteres curinga) neles ou podem começar com - . / p>

Segundo erro:

Em vez de

if [' $x -eq 0 ']

deve ser

if [ "$x" -eq 0 ].

Caso contrário, você receberá um erro

bash: 0: command not found.

Além disso, se você tiver espaços em branco ou curingas nos nomes dos arquivos, ele deverá ser:

rm -i -- "$file2" caso contrário, pode excluir vários arquivos.

    
por 10.11.2018 / 08:40
0

Existem muitas maneiras de resolver isso, mas vou com o que você começou.

Primeiro, não esqueça o lead do script com a string do interpretador ("shebang"):

#!/bin/bash

echo "Enter file 1:"
read file1
echo "Enter file 2:"
read file2
cmp $file1 $file2 > newfile

Neste ponto, você pode testar algumas coisas:

  1. se newfile não estiver vazio, os arquivos serão diferentes
if [ ! -s newfile ]; then
  rm -i $file2
fi
  1. Teste o código de saída para a operação cmp. Se for 0, os arquivos correspondem.
if [ 'echo $?' == 0 ]; then
  rm -i $file2
fi

Além disso, seu comando wc não está funcionando bem. Tente executá-lo fora do script. Você consegue o resultado que está esperando?

    
por 10.11.2018 / 08:32