Zip folder without including path to folder and require only one argument

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.


cd ~/Folder2 && zip -r Folder1 Folder1 && cd -

does exactly what I want. Namely, it zips so that the output 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 (

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 files 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?

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

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 is directory1. (Mnemonic: head and tail.)

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.

coolerzip () (
    cd $1:h &&
    zip -r $ $2:h

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.

coolerzip () (
    name="$(basename -- "$1")"
    cd "$(dirname -- "$1")" &&
    zip -r "$" "$name"

Solution 2

I’m not a zsh-user, but the following works in bash and I don’t think it’s using any particular bashisms, so this should do the job:

coolerzip() { cd "${1%/*}" && zip -r "${1##*/}" "${1##*/}" && cd -; }

$1 is a positional parameter; in a script or a function it will reference the first argument given to the script or function on the command-line. You can also use $2 and so on, up to $9. $0 gives you the name of the script or function (the zeroth argument), [email protected] gives you every argument except the zeroth and $# gives you the number of arguments (again, excluding the zeroth). Things behave a bit differently if you call sh -c (or any shell) from the command-line, but I’m not going to go into that here.

${1%/*} returns the value of $1 with the shortest match of the glob pattern */ stripped away (if you want the longest match at the end, use %%) — so it strips away the final part of the path you feed the function. ${1##*/} strips away the longest match of the pattern */ at the beginning of $1 (for the shortest match at the beginning, use a single #).

The function above is a bit fragile; it’ll break if there are any trailing slashes in the argument (for example ~/folder1/folder2/). It will break in a different way if the argument is a single directory name which doesn’t contain any slashes. An easy way around this is to use the readlink command to return the full path of the input argument (and also strips out any trailing slash, at least on my Ubuntu system).

coolerzip() { a="$(readlink -f "$1")"; cd "${a%/*}" && zip -r "${a##*/}" "${a##*/}" && cd -; }

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from or, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply