0

I am trying to make a command which allows the user to input a name as the first argument for the compressed file they want to create (which will become a tar.gz file) and file and directory names as the second argument.

So far I have this script

name_of_archive=$1
directory_or_file_paths=$2

if [ $# -eq 0 ]
then
        echo "Usage: $0 [name of archive you wish to create] [path of directories or files you wish to compress]"
        echo "You must enter atleast one file or directory name"
        exit 1
else
        if [ -e "$directory_or_file_paths" ] || [ -d "$directory_or_file_paths" ]
        then
                tar -czvf "$name_of_archive".tar.gz "$directory_or_file_paths"
                echo "The filenames or directories you specified have been compressed into an archive called $name_of_archive.tar.gz"
        else
                echo "Some or all of the file or directory names you have given do not exist"
        fi
        exit
fi

This is what I get when I use the command:

compression2.bash compression1 ./test ./listwaste
./test/
./test/test2/
./test/test2/2
./test/1
The filenames or directories you specified have been compressed into an archive called compression1.tar.gz

The first is a directory and the second is a file. It works if I attempt to compress both individually but does not work if I try to compress more than one file or directory or a mix at a time. I would like it to be able to do that.

Lenk
  • 15
  • 2
  • 8

3 Answers3

2

It is not a good idea to store filenames in a string. Storing them in an array is a much better approach:

#!/usr/bin/env bash

[[ $# -lt 2 ]] && exit 1

name=$1; shift
files=("$@")

#exclude all files/directories that are not readable
for index in "${!files[@]}"; do
   [[ -r ${files[index]} ]] || unset "files[index]"
done

[[ ${#files[@]} -eq 0 ]] && exit 1    

if tar -czvf "${name:-def_$$}.tar.gz" "${files[@]}"; then
   echo "Ok"
else
   echo "Error"
   exit 1
fi

shift; files=("$@") discards the first argument (name) and saves the rest of the arguments (filenames) into an array.


You could also construct the array of filenames for tar with a more straightforward approach:

name=$1; shift

for file; do
   [[ -r $file ]] && files+=("$file")
done
PesaThe
  • 7,259
  • 1
  • 19
  • 43
  • the remove files bit will just remove the files from the arguments right and not actually delete them? – Lenk Dec 21 '17 at 01:18
  • @Lenk it will delete them just from the list of files for the `tar` command. Your files will be just fine. I recommend reading on arrays: [wiki.bash-hackers.org](http://wiki.bash-hackers.org/syntax/arrays). – PesaThe Dec 21 '17 at 01:20
  • if i was to make a similar command which only took one variable but multiple arguments I could use this array again right? I am looking to make an archive extraction command aswell. I could base it off this? I dont know what to do for the shift because I think you set it to remove the first argument and I dont want that to happen for this new command. How would I go about doing this? – Lenk Dec 21 '17 at 01:42
  • The first argument is stored in a variable `$name`. Let's say you call your script like this: `./script comp file1 file2`. `$@` is a special variable that holds all arguments, `comp file1 file2` in this case. You save the first argument with `name=$1`. `shift` then discards that argument and what's left in `$@` is this: `file1 file2`. And you save those arguments (filenames) into the array. – PesaThe Dec 21 '17 at 01:45
  • Ah I see. That's very helpful, thank you. So would I just do an array again for my new command but not assign an $1 to a variable as it would all come under the array? I wouldnt need shift then either? – Lenk Dec 21 '17 at 01:51
  • @Lenk I am not exactly sure what you are trying to do. You might want to post a new question if you happen to have problems writing a different script. – PesaThe Dec 21 '17 at 01:58
  • Ok so I am trying to create a similar script to the one you just helped me with, however this time I want to unzip the archive. It will take multiple archives as arguments and unzip them all. Could I use an array here in the same way you just showed me, however this time would I not use shift because I do not need to discard the first argument or any of them actually. I just want all the arguments (archive names) to be passed to the command and the command to execute. I dont need anything to be discarded. – Lenk Dec 21 '17 at 02:03
  • @Lenk yes, you can use an array for that. – PesaThe Dec 21 '17 at 02:28
  • Would there be anything I need to change/add/remove from the script I have now other than the obvious less than n arguments and the -czvf to -xzvf – Lenk Dec 21 '17 at 02:32
  • @Lenk please do not discuss different question in the comments. Post a new one if you have any difficulties with a different script :) – PesaThe Dec 21 '17 at 02:37
1

That is because you are only looking at the 2nd parameter and putting it in your directory_or_file_paths variable. Whenever Linux finds a space in your command, it treats it as another parameter so you are not even looking at these other files or folders. What you will need to do is, if the number of params is not 0, and you have the first one as your name_of_archive, then you will need to loop through all the remaining parameters and construct a string that includes them all, separated by spaces and this is what you will give as a param to the tar command.

  • How would I go about doing this? – Lenk Dec 20 '17 at 22:06
  • Storing the list of files as a string is not safe -- filenames can contain spaces (and tabs and wildcards and...) that'll mess this up. Use an array instead (see @PesaThe's answer). – Gordon Davisson Dec 20 '17 at 23:17
0

I think you want to use shift after getting the first input into your variable name for the archive. You can then pass the whole list, instead of only one argument for the archive.

name_of_archive=$1
shift
directory_or_file_paths=("$@")
...

https://ss64.com/bash/shift.html

mushadow
  • 33
  • 6