Hello, gentlemen. I would like to get advice or help.

The task is as follows - the user enters the name of the script, say, " my_copy " and the list of parameters (still on the command line): an unlimited list of files - directory 1 - directory 2. Suppose:

 my_copy ac bc a1.c b1.c /dir1 /dir2 

implement the function of copying the input files from dir1 to dir2. Actually, my problem is that I do not know how to read the parameters not from the beginning, but from the end. I can find out the number of parameters, but I don’t know how to calculate the last or the last but one.

Well, if someone has a fairly high level and culture of writing beautiful scripts, throw an example, say, for this task. Look at the general design style and generally accepted solutions. Thank you in advance.

    3 answers 3

    The last two parameters can be obtained, for example,

     a1=${!#} #последний a=$(($#-1)) a2=${!a} #предпоследний echo "Last: \"$a1\", before last: \"$a2\"" 

    Still, the shell is a very strange language.

    • Cool, did not know about such a substitution. - avp
    • 2
      I barely remembered. For a long time did not write on the neck. - skegg

    Twisted this way, nothing better (I didn’t really like it myself). But, without shift, spaces in the parameter values ​​do not interfere.

     if [ $# -gt 1 ]; then let last=$# let plast=$last-1 echo nargs = $# lastno = $last plastno = $plast namel=`echo $# | awk '{printf "${%s}",$1}'` vlast=`eval echo $namel` namepl=`echo $plast | awk '{printf "${%s}",$1}'` vplast=`eval echo $namepl` echo last arg = \'$vlast\' before-last = \'$vplast\' else echo not enought fi 

    The names are stupid, but I think the idea with eval is clear.

    UPD @myqck , read your question more closely. Look at copying. Not sure if this is a common pattern , but IMHO is working.

     #!/bin/sh # copy files from dir1 to dir2 # if [ $# -lt 3 ]; then echo Usage: my-cp dir1 dir2 dir1-filenames-list exit 1 fi dir1=$1; shift dir2=$1; shift #echo dir1 = \'$dir1\' dir2 = \'$dir2\' files: $* if [ -d $dir1 ]; then # Это по желанию. Если dir2 должно существовать, выкиньте # и поправьте echo в if mkdir -p $dir2 if [ ! -d $dir2 ]; then echo Can\'t create $dir2 exit 2 fi # copy files with user,permitions,times ... (cd $dir1; tar cf - $*) | (cd $dir2; tar xf -) # другой вариант с cp # dir2=`(cd $dir2; pwd)` # cd $dir1; # cp -R $* $dir2 else echo No source directory: $dir1 exit 1 fi 

    It may be useful to you. To simplify programming, I changed the order of the arguments and allowed copying the hierarchy (if there are tables of contents in the list of copied files)

     my-cp source-dir target-dir files-list-in-source-dir 

    IMHO is also quite convenient for the user. If the target is not, try to make it.

    If copying the hierarchy does not fundamentally suit you, then simply use the copy option with cp and discard -R.

    For the variant with tar, you will have to modify the list (run through it, check the file type and if the item is not a file, then shift).

    • And what, shift is the most important evil on Earth? #! / bin / bash declare -a arr for ((i = 0; $ #> 2; i ++)) do arr [$ i] = $ 1 shift done echo $ {arr [@]} echo $ 1 $ 2 In arr - parameter values ​​(with spaces, if any). How then to disguise them is another question. - alexlz
    • @alexlz, evil? Well, what are you ... It was just interesting for me to remember how you can pull out the i-th argument without touching the others. - avp pm

    Level and culture ... Where can I get them? In general - I do not know. But if there are no spaces in the parameters, then you can assign an array (if there is, then it is somewhat more complicated), and there it’s for indexes -1 and -2

     #!/bin/bash arr=($@) echo ${arr[-1]} ${arr[-2]} 

    But if you need the last two (without previous ones), then you can

     #!/bin/bash shift $(($#-2)) echo $1 $2