cp
과제로 함수(복사)와 기본 기능이 동일한 bash 함수를 교묘하게 작성해 달라는 요청을 받았습니다. 한 파일을 다른 파일로 복사하기만 하면 되므로 여러 파일을 새 디렉터리에 복사할 필요가 없습니다.
저는 bash 언어를 처음 사용하기 때문에 프로그램이 실행되지 않는 이유를 이해할 수 없습니다. 원래 기능은 파일이 이미 존재하는 경우 덮어써야 하므로 이를 구현해 보았습니다. 실패합니다.
파일이 여러 줄에서 실패하는 것 같지만 가장 중요한 것은 복사하려는 파일이 이미 존재하는지 확인하는 것입니다( [-e "$2"]
). 그럼에도 불구하고 조건(파일 이름...)이 충족되면 트리거되어야 하는 메시지가 계속 표시됩니다.
누구든지 이 파일을 수정하는 데 도움을 주고 언어에 대한 기본적인 이해에 대한 유용한 통찰력을 제공할 수 있습니까? 코드는 아래와 같이 표시됩니다.
#!/bin/sh
echo "file variable: $2"
if [-e file]&> /dev/null
then
echo "The file name already exists, want to overwrite? (yes/no)"
read | tr "[A-Z]" "[a-z]"
if [$REPLY -eq "yes"] ; then
rm "$2"
echo $2 > "$2"
exit 0
else
exit 1
fi
else
cat $1 | $2
exit 0
fi
답변1
대상 파일이 이미 존재하는 경우 cp
유틸리티는 사용자에게 메시지를 표시하지 않고 파일을 덮어씁니다.
cp
을 사용하지 않고 기본 기능을 구현하는 함수 cp
는 다음과 같습니다.
cp () {
cat "$1" >"$2"
}
대상을 덮어쓰기 전에 사용자에게 메시지를 표시하려는 경우(비대화형 쉘에서 함수가 호출되는 경우에는 필요하지 않을 수 있음):
cp () {
if [ -e "$2" ]; then
printf '"%s" exists, overwrite (y/n): ' "$2" >&2
read
case "$REPLY" in
n*|N*) return ;;
esac
fi
cat "$1" >"$2"
}
진단 메시지는 표준 오류 스트림으로 전송되어야 합니다. 이것이 제가하는 것입니다 printf ... >&2
.
rm
리디렉션 시 대상 파일이 잘리므로 실제로는 대상 파일이 필요하지 않습니다 . 만약 우리가했다먼저 원하는 경우 rm
디렉터리인지 확인해야 하며, 그렇다면 다음과 같이 해당 디렉터리에 대상 파일을 배치해야 합니다 cp
. 이 작업은 완료되었지만 아직 명시적이지는 않습니다 rm
.
cp () {
target="$2"
if [ -d "$target" ]; then
target="$target/$1"
fi
if [ -d "$target" ]; then
printf '"%s": is a directory\n' "$target" >&2
return 1
fi
if [ -e "$target" ]; then
printf '"%s" exists, overwrite (y/n): ' "$target" >&2
read
case "$REPLY" in
n*|N*) return ;;
esac
fi
cat "$1" >"$target"
}
소스가 실제로 존재하는지 확인하고 싶을 수도 있습니다.cp
하다do ( cat
이 작업도 수행하므로 완전히 생략할 수 있지만 그렇게 하면 빈 개체 파일이 생성됩니다):
cp () {
if [ ! -f "$1" ]; then
printf '"%s": no such file\n' "$1" >&2
return 1
fi
target="$2"
if [ -d "$target" ]; then
target="$target/$1"
fi
if [ -d "$target" ]; then
printf '"%s": is a directory\n' "$target" >&2
return 1
fi
if [ -e "$target" ]; then
printf '"%s" exists, overwrite (y/n): ' "$target" >&2
read
case "$REPLY" in
n*|N*) return ;;
esac
fi
cat "$1" >"$target"
}
이 함수는 "bashisms"을 사용하지 않으며 sh
모든 유사한 쉘에서 작동합니다.
여러 소스 파일을 지원하기 위한 추가 조정과 -i
기존 파일을 덮어쓸 때 대화형 프롬프트를 활성화하는 플래그:
cp () {
local interactive=0
# Handle the optional -i flag
case "$1" in
-i) interactive=1
shift ;;
esac
# All command line arguments (not -i)
local -a argv=( "$@" )
# The target is at the end of argv, pull it off from there
local target="${argv[-1]}"
unset argv[-1]
# Get the source file names
local -a sources=( "${argv[@]}" )
for source in "${sources[@]}"; do
# Skip source files that do not exist
if [ ! -f "$source" ]; then
printf '"%s": no such file\n' "$source" >&2
continue
fi
local _target="$target"
if [ -d "$_target" ]; then
# Target is a directory, put file inside
_target="$_target/$source"
elif (( ${#sources[@]} > 1 )); then
# More than one source, target needs to be a directory
printf '"%s": not a directory\n' "$target" >&2
return 1
fi
if [ -d "$_target" ]; then
# Target can not be overwritten, is directory
printf '"%s": is a directory\n' "$_target" >&2
continue
fi
if [ "$source" -ef "$_target" ]; then
printf '"%s" and "%s" are the same file\n' "$source" "$_target" >&2
continue
fi
if [ -e "$_target" ] && (( interactive )); then
# Prompt user for overwriting target file
printf '"%s" exists, overwrite (y/n): ' "$_target" >&2
read
case "$REPLY" in
n*|N*) continue ;;
esac
fi
cat -- "$source" >"$_target"
done
}
코드의 간격이 잘못되었습니다 ( if [ ... ]
앞, 뒤 , 앞에 [
공백이 필요함 ]
). 또한 /dev/null
테스트 자체에는 출력이 없으므로 테스트를 다음으로 리디렉션하려고 시도해서는 안 됩니다 . 또한 첫 번째 테스트에서는 $2
문자열이 아닌 위치 매개변수를 사용해야 합니다 file
.
내가 했던 것처럼 사용하면 case ... esac
사용자의 소문자/대문자 응답을 사용하지 않아도 됩니다 tr
. bash
어쨌든 그렇게 하고 싶다면 (대문자의 경우) 또는 (소문자의 경우)를 사용하는 것이 더 저렴한 방법 REPLY="${REPLY^^}"
입니다 REPLY="${REPLY,,}"
.
사용자가 코드에 대해 "예"라고 대답하면 함수는 대상 파일의 파일 이름을 대상 파일에 넣습니다. 이것은 소스 파일의 복사본이 아닙니다. 함수의 실제 복사 비트에 속해야 합니다.
복사 비트는 파이프를 사용하여 구현하는 것입니다. 파이프는 한 명령의 출력에서 다른 명령의 입력으로 데이터를 전달하는 데 사용됩니다. 우리가 여기서 해야 할 일은 그게 아닙니다. 소스 파일을 호출 cat
하고 출력을 대상 파일로 리디렉션하기만 하면 됩니다.
이전에 나에게 전화했을 때도 같은 문제가 있었습니다 tr
. read
변수의 값은 설정되지만 출력은 생성되지 않으므로 read
아무 것도 파이프로 연결하는 것은 의미가 없습니다.
사용자가 "아니요"라고 말하지 않는 한 명시적으로 종료할 필요는 없습니다(또는 내 코드 비트에 표시된 것처럼 함수가 일부 오류 조건을 만나지만 이 함수는 내가 사용하는 함수이기 때문에 그렇지 return
않습니다 exit
).
또한 "함수"라고 말했지만 구현은 스크립트입니다.
보세요https://www.shellcheck.net/, 이는 쉘 스크립트의 문제가 있는 부분을 식별하는 훌륭한 도구입니다.
cat
그냥 사용하나파일의 내용을 복사하는 방법입니다. 다른 방법은 다음과 같습니다
dd if="$1" of="$2" 2>/dev/null
sed "" "$1" >"2"
필터와 같은 유틸리티를 사용하면 또는awk '1' "$1" >"$2"
등과tr '.' '.' <"$1" >"$2"
같은 데이터만 전달하도록 할 수 있습니다 .- 등.
까다로운 부분은 소스에서 대상으로 메타데이터(소유권 및 권한)를 복사하는 기능을 갖는 것입니다.
cp
주목해야 할 또 다른 점은 /dev/tty
대상이 (비정규 파일)과 같은 경우 내가 작성한 함수가 완전히 다르게 작동한다는 것입니다.