Сценарий для обмена именами двух файлов

Я новичок в сценариях Bash. Я пытаюсь создать скрипт, который заменит имена файлов двух файлов, переданных ему пользователем.

Вот изображение двух версий моего скрипта до сих пор

Вот сценарий в текстовом формате с 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

Но мой вопрос в том, могу ли я создать скрипт, который позволит пользователю записать сразу два имени файла, а затем скрипт заменит 2 имена файлов

Основными именами файлов подкачки, которые я прочитал, является это

cp $file1 temporaryfile mv $file1 $file2 mv $file2 temporyfile
7
задан 5 December 2017 в 18:42

4 ответа

Вы можете использовать расширение параметра для задачи, если получаете два имени файла, или вы можете прочитать их в скрипте. мой примерный сценарий использует расширение параметра. И вы можете использовать временный каталог для своих параметров перемещения, потому что, если имя файла, используемое в скрипте, уже существует, этот файл будет тихо перезаписан.

#!/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="$1" file2="$2" # swapping the files # first move and rename file1 to $TMP/tempfile mv "$1" $TMP/tempfile # renaming file2 to file1 name mv "$2" "$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

Из Bash-Manual # Shell-Parameters :

Параметр - это объект, который сохраняет значения. Это может быть имя, номер или один из специальных символов, перечисленных ниже. Переменная - это параметр, обозначаемый именем. Переменная имеет значение и ноль или более атрибутов. Атрибуты назначаются с помощью команды declare builtin (см. Описание встроенного объявления в Bash Builtins). Параметр устанавливается, если ему присвоено значение. Нулевая строка является допустимым значением. Как только переменная установлена, она может быть отменена только с помощью команды unset builtin. Переменной может присваиваться оператор формы name = [value]

И если вы хотите прочитать в именах файлов из диалогового диалогового окна:

#!/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

Из Bash-Manual # Shell-Parameters :

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

Параметр - это объект, который хранит значения. Это может быть имя, номер или один из специальных символов, перечисленных ниже. Переменная - это параметр, обозначаемый именем. Переменная имеет значение и ноль или более атрибутов.

Одна строка считывается со стандартного ввода или из файлового дескриптора fd, поставляемого в качестве аргумент опции -u, разбитый на слова, как описано выше в [Разделение слов] [6], и первое слово присваивается первому имени, второе слово - второму имени и т. д. Если слов больше, чем имен, остальные слова и их промежуточные разделители присваиваются фамилии. Если из входного потока меньше слов, чем имен, оставшимся именам присваиваются пустые значения. Символы в значении переменной IFS используются для разделения строки на слова с использованием тех же правил, которые оболочка использует для расширения (описано выше в [Разделение слов] [6]). Символ обратной косой черты «\» может использоваться для удаления любого специального значения для следующего считывания символов и продолжения строки. Если имена не указаны, чтение строки присваивается переменной REPLY.

-r → Если эта опция задана, обратная косая черта не действует как escape-символ. Обратная косая черта считается частью линии. В частности, пара обратной косой черты не может использоваться в качестве продолжения строки. -p prompt → Перед тем, как читать какие-либо данные, покажите приглашение без конечной новой строки. Приглашение отображается только в том случае, если вход поступает с терминала.

read принимает несколько параметров. В этом случае два наиболее релевантных, так как вы хотите задать пользователю вопрос и получить от них информацию. Эти опции:

6
ответ дан 18 July 2018 в 01:49

Один из возможных способов сделать это

В то время как я создал функцию специально для этой цели, которую я сохраняю в своем .bashrc, и ее можно адаптировать в сценарий. Вы должны использовать позиционные параметры, чтобы пользователи могли помещать имена файлов в командной строке. Вот моя оригинальная функция:

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

Вы можете избавиться от объявления swap_files(){, ключевого слова local и закрыть } и превратить его в скрипт - просто добавьте #!/bin/bash в Вверх. Конечно, есть тонны вещей, которые можно улучшить, но на самом базовом уровне это примерно так же просто, как и обмен swapping (который, кстати, часто обучается на C для замены элементов массива, но это только касательная тема).

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

Конечно, не забудьте указать позиционные параметры, если имена файлов содержат пробелы. Например:

swap_files 'file 1' 'file 2'

Обратите внимание на использование --, чтобы избежать проблем с именами файлов, у которых есть ведущие -. Лучше всего было бы привыкнуть к ссылке на файлы в текущем рабочем каталоге с помощью ./, особенно если вы используете globstar * (globstar не имеет отношения к этому вопросу, но стоит упомянуть, если мы говорим об именах файлов с ведущим -). Кроме того, путь ./ более переносим, ​​поскольку некоторые версии mv, такие как позиционные параметры , не имеют опции --.

Как было предложено terdon в комментариях, мы также можем создать временный файл в родительской папке первого файла, чтобы избежать перемещения файлов через файловые системы.

#!/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" = "$1" ]; then file1_dir="." fi tmpfile=$(mktemp -p "$file1_dir" ) mv -- "$1" "$tmpfile" mv -- "$2" "$1" mv -- "$tmpfile" "$2" fi

Ваш сценарий и все, что нужно улучшить

1. Резервированное присвоение переменной

file1=$file1 file2=$file2

Эта часть назначает переменную $file1 переменной ... file1; есть две проблемы с этим: присваивание переменной самому себе избыточно, и для начала этого не существует, в скрипте уже нет объявления этой переменной.

2. Остерегайтесь разрыва слова с помощью read

. Вот что произойдет, если ваш пользователь попытается включить даже указанные элементы в вашу команду read:

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

В соответствии с поведением оболочки , оболочка разделяет все, что читается на stdin, и пытается вставить каждое слово в соответствующие переменные, а если слова превышают число переменных - он пытается вставить все в последнюю переменную. Я рекомендую вам читать в каждом файле по одному.

3. Копирование в себя является ошибкой

Вы делаете

cp $file1 $file1;

Это приведет к ошибке

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

Возможно, вы хотели сделать

cp "$file1" "$file1".tmp

Или просто используйте команду mktemp, как и я. Также обратите внимание на цитирование переменных, чтобы предотвратить разделение слов.

Другие интересные способы сделать это

Знаете ли вы, что вы можете котировать любой файл с перенаправлением на сделать копию? Поэтому использование mv или cp не является единственным способом. Что-то вроде этого:

$ cat ./wallpaper.jpg > wallpaper.jpg.tmp $ cat ./screenshot.jpg > wallpaper.jpg $ cat ./wallpaper.jpg.tmp > ./screenshot.jpg
11
ответ дан 18 July 2018 в 01:49

Вы можете использовать расширение параметра для задачи, если получаете два имени файла, или вы можете прочитать их в скрипте. мой примерный сценарий использует расширение параметра. И вы можете использовать временный каталог для своих параметров перемещения, потому что, если имя файла, используемое в скрипте, уже существует, этот файл будет тихо перезаписан.

#!/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="$1" file2="$2" # swapping the files # first move and rename file1 to $TMP/tempfile mv "$1" $TMP/tempfile # renaming file2 to file1 name mv "$2" "$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

Из Bash-Manual # Shell-Parameters :

Параметр - это объект, который сохраняет значения. Это может быть имя, номер или один из специальных символов, перечисленных ниже. Переменная - это параметр, обозначаемый именем. Переменная имеет значение и ноль или более атрибутов. Атрибуты назначаются с помощью команды declare builtin (см. Описание встроенного объявления в Bash Builtins). Параметр устанавливается, если ему присвоено значение. Нулевая строка является допустимым значением. Как только переменная установлена, она может быть отменена только с помощью команды unset builtin. Переменной может присваиваться оператор формы name = [value]

И если вы хотите прочитать в именах файлов из диалогового диалогового окна:

#!/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

Из Bash-Manual # Shell-Parameters :

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

Параметр - это объект, который хранит значения. Это может быть имя, номер или один из специальных символов, перечисленных ниже. Переменная - это параметр, обозначаемый именем. Переменная имеет значение и ноль или более атрибутов.

Одна строка считывается со стандартного ввода или из файлового дескриптора fd, поставляемого в качестве аргумент опции -u, разбитый на слова, как описано выше в [Разделение слов] [6], и первое слово присваивается первому имени, второе слово - второму имени и т. д. Если слов больше, чем имен, остальные слова и их промежуточные разделители присваиваются фамилии. Если из входного потока меньше слов, чем имен, оставшимся именам присваиваются пустые значения. Символы в значении переменной IFS используются для разделения строки на слова с использованием тех же правил, которые оболочка использует для расширения (описано выше в [Разделение слов] [6]). Символ обратной косой черты «\» может использоваться для удаления любого специального значения для следующего считывания символов и продолжения строки. Если имена не указаны, чтение строки присваивается переменной REPLY.

-r → Если эта опция задана, обратная косая черта не действует как escape-символ. Обратная косая черта считается частью линии. В частности, пара обратной косой черты не может использоваться в качестве продолжения строки. -p prompt → Перед тем, как читать какие-либо данные, покажите приглашение без конечной новой строки. Приглашение отображается только в том случае, если вход поступает с терминала.

read принимает несколько параметров. В этом случае два наиболее релевантных, так как вы хотите задать пользователю вопрос и получить от них информацию. Эти опции:

6
ответ дан 24 July 2018 в 17:28
  • 1
    Пробелы допустимы после #! на самом деле, см. unix.stackexchange.com/q/276751/85039 в противном случае хороший ответ – Sergiy Kolodyazhnyy 5 December 2017 в 12:09
  • 2
    mv $TMP/tempfile "$file2" && предотвратит удаление $TMP в случае, если mv может успешно завершиться (представить полный диск) - или как насчет [ -e $TMP/tempfile ] && rm -r $TMP || echo "Error!"? – dessert 5 December 2017 в 13:48
  • 3
    В вашем втором скрипте все еще есть $1 и $2 ... Btw, в чем польза $file1 и $file2 в первом? – dessert 5 December 2017 в 13:50

Один из возможных способов сделать это

В то время как я создал функцию специально для этой цели, которую я сохраняю в своем .bashrc, и ее можно адаптировать в сценарий. Вы должны использовать позиционные параметры, чтобы пользователи могли помещать имена файлов в командной строке. Вот моя оригинальная функция:

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

Вы можете избавиться от объявления swap_files(){, ключевого слова local и закрыть } и превратить его в скрипт - просто добавьте #!/bin/bash в Вверх. Конечно, есть тонны вещей, которые можно улучшить, но на самом базовом уровне это примерно так же просто, как и обмен swapping (который, кстати, часто обучается на C для замены элементов массива, но это только касательная тема).

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

Конечно, не забудьте указать позиционные параметры, если имена файлов содержат пробелы. Например:

swap_files 'file 1' 'file 2'

Обратите внимание на использование --, чтобы избежать проблем с именами файлов, у которых есть ведущие -. Лучше всего было бы привыкнуть к ссылке на файлы в текущем рабочем каталоге с помощью ./, особенно если вы используете globstar * (globstar не имеет отношения к этому вопросу, но стоит упомянуть, если мы говорим об именах файлов с ведущим -). Кроме того, путь ./ более переносим, ​​поскольку некоторые версии mv, такие как позиционные параметры , не имеют опции --.

Как было предложено terdon в комментариях, мы также можем создать временный файл в родительской папке первого файла, чтобы избежать перемещения файлов через файловые системы.

#!/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" = "$1" ]; then file1_dir="." fi tmpfile=$(mktemp -p "$file1_dir" ) mv -- "$1" "$tmpfile" mv -- "$2" "$1" mv -- "$tmpfile" "$2" fi

Ваш сценарий и все, что нужно улучшить

1. Резервированное присвоение переменной

file1=$file1 file2=$file2

Эта часть назначает переменную $file1 переменной ... file1; есть две проблемы с этим: присваивание переменной самому себе избыточно, и для начала этого не существует, в скрипте уже нет объявления этой переменной.

2. Остерегайтесь разрыва слова с помощью read

. Вот что произойдет, если ваш пользователь попытается включить даже указанные элементы в вашу команду read:

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

В соответствии с поведением оболочки , оболочка разделяет все, что читается на stdin, и пытается вставить каждое слово в соответствующие переменные, а если слова превышают число переменных - он пытается вставить все в последнюю переменную. Я рекомендую вам читать в каждом файле по одному.

3. Копирование в себя является ошибкой

Вы делаете

cp $file1 $file1;

Это приведет к ошибке

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

Возможно, вы хотели сделать

cp "$file1" "$file1".tmp

Или просто используйте команду mktemp, как и я. Также обратите внимание на цитирование переменных, чтобы предотвратить разделение слов.

Другие интересные способы сделать это

Знаете ли вы, что вы можете котировать любой файл с перенаправлением на сделать копию? Поэтому использование mv или cp не является единственным способом. Что-то вроде этого:

$ cat ./wallpaper.jpg > wallpaper.jpg.tmp $ cat ./screenshot.jpg > wallpaper.jpg $ cat ./wallpaper.jpg.tmp > ./screenshot.jpg
11
ответ дан 24 July 2018 в 17:28
  • 1
    Вы не храните временный файл, вы mv его. Кроме того, для еще одного улучшения вы можете использовать dir1="${file1%/*}"; tmpfile="$(mktemp -p "$dir1")", чтобы временный файл всегда создавался в той же файловой системе, что и исходный файл, чтобы избежать mv на границах файловой системы. – terdon♦ 5 December 2017 в 12:56
  • 2
    Просто примечание: Tempfiles, созданные с помощью mktemp never , содержат элементы n-типа в виде пробелов, поэтому цитирование $TMPFILE не требуется; с другой стороны, хорошей практикой является просто указать любой путь и имя файла, скрипт. – dessert 5 December 2017 в 13:17
  • 3
    @dessert Хорошая точка. Хотя есть больше цитат, чем просто пробелов. Вас может заинтересовать unix.stackexchange.com/q/171346/85039 – Sergiy Kolodyazhnyy 5 December 2017 в 13:27

Другие вопросы по тегам:

Похожие вопросы: