0

I'm trying to remove some specifics characters from a string. The idea is that the user inputs permissions and a file name and I remove the bad permissions (those not being r, w or x). My script would be as it follows:

permisos=$1  
for i in `seq 1  ${#1}`; do  
        perm_actual=${1:i-1:1}  
        [[ $perm_actual != *"r"* ]] && [[ $perm_actual != *"w"* ]] && [[ $perm_actual != *"x"* ]] && echo "El permiso $perm_actual es incorrecto, ignorándolo..."  
        permisos=${permisos/perm_actual/}  
done  

chmod u+$permisos $2 

Does anybody know why is not removing the bad permissions from the variable permisos?

Thanks in advance

Kusalananda
  • 333,661

2 Answers2

4

Removing letters from strings is easy to do in one go without iterating over the characters of the string. This could be done with tr like this:

#!/bin/sh

perms="$( printf '%s' "$1" | tr -c -d 'rwx' )"

chmod u+"$perms" "$2"

This uses tr to first remove everything from the first command line argument that is not any of the characters r, w, or x, then it performs the chmod.

Note the quoting of the expansions, especially "$2". Without these quotes, you would not be able to use your script on files with spaces or filename globbing characters in their names (see e.g. "Why does my shell script choke on whitespace or other special characters?").

In bash, you may instead use a parameter substitution like

perms=${1//[!rwx]/}

This removes everything that is not r, w or x from $1 and assigns the result to perms.

The bash script then becomes

#!/bin/bash
chmod u+"${1//[!rwx]/}" "$2"

By using

shift
chmod u+"$perms" "$@"

where $perms is computed in any of the above ways (before the shift), you could even get your script to perform the change on multiple files at once. The shift removes the first command line argument form the list and "$@" will expand to the rest of the arguments, each individually quoted.


If you absolutely need to iterate over the characters of the string (you don't), then you may do so like this:

#!/bin/bash

string="$1"

for (( i = 0; i < ${#string}; ++i )); do
    ch="${string:i:1}"
    if [[ "$ch" == [rwx] ]] && [[ "$new_string" != *$ch* ]]; then
        new_string+=$ch
    fi
done

printf 'The new string is "%s"\n' "$new_string"

This builds a new string from the old by adding r, w, or x to it if the current character in the original string is one of those characters, and if the character does not already exist in the new string (not necessary for the operation of chmod).

Instead of using seq, we use a for loop running from zero to one less than the original string's length, and for ease of reading and to not repeat ourselves, we extract the current character into ch in each iteration.

Instead of three tests on the character, we perform a single pattern match to see whether it's r, w or x.

Kusalananda
  • 333,661
2

${permisos/perm_actual/} removes the literal string perm_actual from the variable's value, I think you want ${permisos/$perm_actual/} instead.

But you could just use permisos=${permisos//[^rwx]} instead of the loop to remove every non-rwx character at one go, and [[ $permisos = *[^rwx]* ]] to check if the variable has any of them.

(Both ${foo//bar} and [[ .. ]] are nonstandard extensions, but you used them already so you're probably running Bash/ksh/zsh.)

ilkkachu
  • 138,973