-1

Could you help me to create bash script to: Set the permissions in the directory that is the argument of the script for all files that have a defined extension as the second script argument to values that are defined as the third argument of the script.

Mirko Steiner
  • 354
  • 1
  • 5
  • 2
    Please, show us the effort that you put into solving this problem yourself - examples of what you tried and failed and such. – Dzienny Feb 05 '19 at 18:24
  • 1
    Is your problem in parsing the arguments (as in https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash) or in changing the permissions (read `man chmod`)? – Ljm Dullaart Feb 05 '19 at 18:27

2 Answers2

1

Since you're probably new to bash and parsing arguments passed to scripts, I'll show you a rudimentary way to accomplish what you are describing.

#!/bin/bash

# stop execution of the script if an error occurs suchs as when the 
# directory in argument 1 does not exists
set -e 

dir=$1 # get the directory from the first argument
ext=$2 # get the extension from the second argument
perms=$3 # the third argument is the permissions you're going to pass to `chmod`

cd "$dir" # change directory to the target directory

# use regular filename expansion with the extension in 
# $ext and supply `chmod` with the permissions in $perms
chmod "$perms" *"$ext" 

If you saved this as extchmod.sh and made it executable, you would run it like this:

$ ./extchmod.sh target_directory .txt 644
$ ./extchmod.sh target_directory .sh 755

which would change all files in target_directory with a .txt extensions to permissions 644 and all files with a .sh extension to permissions 755.

I should note that in bash/sh, $1 has the value of the first argument, $2 has the value of the second argument, and so on. $@ will always be an array containing all of the arguments.

Bill Doughty
  • 2,250
  • 14
  • 20
  • 1
    `cd "$DIR"`, and `chmod "$PERMS" *"$EXT"`, or arguments with spaces will be trouble. – Charles Duffy Feb 05 '19 at 18:43
  • 1
    Also, as a matter of teaching/demonstrating best practices, all-caps variable names are used for names meaningful to the shell and OS-provided tools, whereas lowercase names are guaranteed not to conflict with behavior of POSIX-defined tools. See spec @ http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html (the paragraph including the sentence *"The name space of environment variable names containing lowercase letters is reserved for applications"*). – Charles Duffy Feb 05 '19 at 18:44
  • @CharlesDuffy [Google shell style guide](https://google.github.io/styleguide/shell.xml?showone=Constants_and_Environment_Variable_Names#Constants_and_Environment_Variable_Names) suggest the use of all-caps names for constants as well. – Dzienny Feb 05 '19 at 19:04
  • POSIX has been around a lot longer than Google has. That said, note that they're referring only to *environment* variables (and constants, which these values aren't), not *local* variables. You aren't `export`ing `DIR`, so it's a regular shell variable, not an environment variable (unless the name overlaps a preexisting environment variable or shell builtin -- preventing such conflicts being the point of following the POSIX guideline linked above for *all* shell variables). – Charles Duffy Feb 05 '19 at 19:05
  • @CharlesDuffy Thanks for the reference to using lower case for local variables. I wanted to highlight the variables for clarity, but after consideration, I believe you are right about teaching best practices. I have updated my answer. – Bill Doughty Feb 05 '19 at 19:23
  • If the provided dir is invalid, or does not exist, it will change permissions in the cwd. – Dzienny Feb 05 '19 at 19:25
  • @CharlesDuffy It is rather a guideline for the os devs. The quoted statement does not imply that environment variable names consisting of all upper case letters are reserved only for the os use. Although, it may be prudent not to declare all upper case variables. – Dzienny Feb 05 '19 at 19:54
  • @Dzienny Good catch. Answer updated with the simplest solution using `set -e`. The most user friendly version of a script like this would be far beyond the point of the example. Ideally, it would accept a help flag, display usage information, capture stderr, check return codes of commands, and conditionally echo helpful error messages and stop execution if a command fails. Ultimately, I probably shouldn't have answered this question as it doesn't meet the SO guidelines, but I wanted to be helpful and give some pointers, since @Martin is probably new to scripting all together. – Bill Doughty Feb 05 '19 at 19:56
  • I edited the script and added `|| exit 1` to prohibit chmod in current directory, if `cd $dir` fails (dir not exists or entry forbidden). ---- damn, haven't seen `set -e` – Wiimm Feb 05 '19 at 20:17
1

I would recommend using a combination of find and xargs

  • Directory: /home/mirko/example/
  • Fileextension: .jpg
  • Mode for files: 644

$ find /home/mirko/example/ -maxdepth 1 -name '*.jpg' -print0 | xargs -0 chmod 644

If you still want a shell script for that, I would suggest something like:

#!/usr/bin/env bash

scriptname=$(basename $0)

if [ $# -ne 3 ]; then
    echo "usage: $scriptname path extension mode" >&2
    echo "example: $scriptname /home/foo/pictures/ jpg 644" >&2
    exit 1
fi

directory=$1
extension=$2
mode=$3

find "$directory" -maxdepth 1 -name "*.${extension}" -print0 | xargs -0 chmod "$mode"

if [ $? -ne 0 ]; then
    echo "$scriptname: ERROR: command returned unsuccesfull" >&2
    exit 1
fi
Mirko Steiner
  • 354
  • 1
  • 5
  • `xargs` is superfluous here. You can use the `-exec` option of `find` – hek2mgl Feb 06 '19 at 08:47
  • You need to quote `${directory}` and `${mode}` when accessing them to prevent from word splitting to happen. – hek2mgl Feb 06 '19 at 08:49
  • @hek2mgl due to you don't know how many files are to process, it is safer to use xargs. Also `-exec` from `find` iterates over each finding, and `xargs` processes multiple findings at once. Actually im not sure, if `-exec` and spaces (0x20) in filenames works, so the `-print0` and `-0` is definitly safe. – Mirko Steiner Feb 06 '19 at 09:11
  • @hek2mgl You _can_ use the curly braces also when you don't need them. sometime i used them to make things easier to read. – Mirko Steiner Feb 06 '19 at 09:16
  • (1) `find -exec` _is_ safe. (2) I'm talking about quotes, not curly braces – hek2mgl Feb 06 '19 at 09:20
  • (1) processing 5000 files with `-exec` would call 5000 times `chmod` and just 1 time with `xargs`. I did a test with 5000 files, `xargs` tooked 50ms and `-exec` tooked 3500ms. I stay with xargs. (2) Quotes added. I always fight with my self, if I should post code here anyway. People just copy and paste the code and use it without understanding it. So I just try to give an idea (thats why i wrote _I would suggest something like_) insteaed of an waterproof, 100% reliable solution – Mirko Steiner Feb 06 '19 at 09:37
  • 1
    Nice to see that you quoted the variables because that was actually the most concerning part. Note that `find ... -exec chmod {} +` (note the `+` at the end), will call `chmod` *once*. Anyhow, `xargs` will perform as good as that. Meaning it is a matter of preference. At least you should know now about `find ... -exec {} +` – hek2mgl Feb 06 '19 at 20:55