Я новичок в сценариях 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
Вы можете использовать расширение параметра для задачи, если получаете два имени файла, или вы можете прочитать их в скрипте. мой примерный сценарий использует расширение параметра. И вы можете использовать временный каталог для своих параметров перемещения, потому что, если имя файла, используемое в скрипте, уже существует, этот файл будет тихо перезаписан.
#!/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 принимает несколько параметров. В этом случае два наиболее релевантных, так как вы хотите задать пользователю вопрос и получить от них информацию. Эти опции:
В то время как я создал функцию специально для этой цели, которую я сохраняю в своем .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
file1=$file1
file2=$file2
Эта часть назначает переменную $file1 переменной ... file1; есть две проблемы с этим: присваивание переменной самому себе избыточно, и для начала этого не существует, в скрипте уже нет объявления этой переменной.
. Вот что произойдет, если ваш пользователь попытается включить даже указанные элементы в вашу команду read:
$ read file1 file2
'one potato' 'two potato'
$ echo "$file1"
'one
$ echo "$file2"
potato' 'two potato'
В соответствии с поведением оболочки , оболочка разделяет все, что читается на stdin, и пытается вставить каждое слово в соответствующие переменные, а если слова превышают число переменных - он пытается вставить все в последнюю переменную. Я рекомендую вам читать в каждом файле по одному.
Вы делаете
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
Вы можете использовать расширение параметра для задачи, если получаете два имени файла, или вы можете прочитать их в скрипте. мой примерный сценарий использует расширение параметра. И вы можете использовать временный каталог для своих параметров перемещения, потому что, если имя файла, используемое в скрипте, уже существует, этот файл будет тихо перезаписан.
#!/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 принимает несколько параметров. В этом случае два наиболее релевантных, так как вы хотите задать пользователю вопрос и получить от них информацию. Эти опции:
В то время как я создал функцию специально для этой цели, которую я сохраняю в своем .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
file1=$file1
file2=$file2
Эта часть назначает переменную $file1 переменной ... file1; есть две проблемы с этим: присваивание переменной самому себе избыточно, и для начала этого не существует, в скрипте уже нет объявления этой переменной.
. Вот что произойдет, если ваш пользователь попытается включить даже указанные элементы в вашу команду read:
$ read file1 file2
'one potato' 'two potato'
$ echo "$file1"
'one
$ echo "$file2"
potato' 'two potato'
В соответствии с поведением оболочки , оболочка разделяет все, что читается на stdin, и пытается вставить каждое слово в соответствующие переменные, а если слова превышают число переменных - он пытается вставить все в последнюю переменную. Я рекомендую вам читать в каждом файле по одному.
Вы делаете
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