I'm trying to make a zip function that'll do all the steps I typically do when zipping a file or folder. Say my current directory is ~
, and I want to zip a folder named Folder1
, whose file path is ~/Folder2/Folder1
.
Issuing
cd ~/Folder2 && zip -r Folder1 Folder1 && cd -
does exactly what I want. Namely, it zips so that the output Folder1.zip
does not contain folders that lead up to the folder I'm zipping (namely, /home/user/Folder2
), it zips recursively, and it takes in the first argument of zip with the exact name as the file or folder I'm zipping (Folder1.zip
).
So I want to make a function in my .zshrc
something akin to
coolerzip() { cd /path/to/file && zip -r file file && cd - }
where it need only take in one argument, which in this case, it will be ~/Folder2/Folder1
. Then Folder1
will be the name for both file
s in coolerzip
, and I will cd
right into ~/Folder2
so Folder1
lies in my current directory before zipping. Is there any way I can achieve this so that coolerzip
only requires one argument rather than three?
Best Answer
The argument of the function is
$1
.You can use history expansion modifiers to extract the directory part and the last component of the path: if the argument is
~/directory2/directory1
then$1:h
is~/directory2
and$1:t
isdirectory1
. (Mnemonic:h
ead andt
ail.)Use parentheses instead of braces around the function body. This way the function body runs in a subshell (a separate shell process), and variable assignments, directory changes and so on only affect the subshell.
Other shells don't have history expansion modifiers, so you'd need something different to parse the argument. For the file name,
${1##*/}
strips off all leading directory components; however, this doesn't work if there's a trailing slash in the parameter, whereas"$(basename -- "$1")"
works in that case. For the leading directories, use$(dirname -- "$1")
. Note that in shells other than zsh, you need to put double quotes around variable substitutions.