LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Newbie (https://www.linuxquestions.org/questions/linux-newbie-8/)
-   -   Shell script variables (https://www.linuxquestions.org/questions/linux-newbie-8/shell-script-variables-496884/)

Fredde87 10-30-2006 07:24 AM

Shell script variables
 
Hi

I have a script which might have some variables in it depending on what the user has chosen. The variables will be in in format $RemoveN, N being an even number (like $Remove2, $Remove4, $Remove 6 and so forth). they will equal Remove, and the N represents a line number in a file. I need to delete this line when N=Remove. So if $Remove4 is null, do nothing, but if $Remove6 eq Remove then delete line 6 from file /var/user/rules. Any simple way to do this? I could always go for the easy option and do a long list where he checks every variable at a time but I thought there must be a easier way to do it.

unSpawn 10-30-2006 08:21 AM

Code:

: ${Remove66=}
: ${Remove4=}
: ${Remove1=}
vars_set=${!Remove*}


Fredde87 10-30-2006 09:54 AM

Sorry I am fairley new to this. Could you give me a quick description to as what it does and how I can implement it?

unSpawn 10-30-2006 10:50 AM

Well, you just said you had the variable names and didn't talk about their values, so I had to make some (empty) variables to make it work as an example:
Code:

: ${Remove66=}
: ${Remove4=}
: ${Remove1=}

The "${!Remove*}" was a hint so you could:
Code:

for number in ${!Remove*}; do
 echo "sed ${number//Remove/}d file \
 > file.tmp && mv file.tmp file"
done

I think you need a reverse sort though, else once you remove one line the other line numbers get skewed.

Fredde87 10-30-2006 11:15 AM

Thank you for your reply. I gave that an try but it just spat out a syntax error, bad substitution.

unSpawn 10-30-2006 12:12 PM

I gave that an try but it just spat out a syntax error, bad substitution.
Please dont *talk* about errors but *post* the exact one.

Fredde87 10-31-2006 03:25 AM

Sorry but that is the exact one, it says "Syntax Error: Bad substitution". My /bin/sh doesnt support substitution that is why. Is there anyother way to achive what I am looking to do?


Regards

Fredrik

unSpawn 10-31-2006 04:53 AM

Sorry but that is the exact one, it says "Syntax Error: Bad substitution". My /bin/sh doesnt support substitution that is why. Is there anyother way to achive what I am looking to do?
Are you saying you're using Bourne and not Bash?
Sure there will be another way.
Could you post a download location for the script?

Fredde87 10-31-2006 05:41 AM

I havent got a script yet... It is going to be a cgi script which gets directed to when the user presses a button (or perhaps if a user developes an application to delete several lines at once). This is what it looks like so far,
Code:

#!/bin/sh

QUERY_STRING=$(cat)
if [ -n "$QUERY_STRING" ]; then
  eval `query_string2sh "$QUERY_STRING"`
  # script goes here
fi

So nothing created yet. Here is the content of query_string2sh,

Code:

#!/bin/sh

[ $# -le 1 ] || { echo "Usage: $0 [query_string]" >&2; exit 2; }
qs=${1-"$QUERY_STRING"}

printf "%s\n" "$qs" \
| tr '&+' '\012 ' \
| sed -n 's/^\([A-Za-z_][A-Za-z0-9_]*\)=/\1 /p' \
| while read var value
  do value=`echo "$value" | sed "s/'/'\\\\\\''/g"`
    echo "PARAM_$var='$value'"
  done


So what query_strin2sh does is it will spit out eveything sent in the POST to its own variable, so if textfield=Test%20123 then there will be a variable called $PARAM_textfield with the value of "Test 123". Now this script I am creating now is for the remove button. There can be as many Remove buttons as the user wants (depends on how many lines there is in a config file). They are all called by the following serie, Remove2, Remove4, Remove6, Remove8, Remove10 and so forth. They will either not have a value att all (button not pressed) or string "Remove" (button is called Remove, so the label is sent as the value). Now if we have the following case scenario
PARAM_Remove2=
PARAM_Remove4=
PARAM_Remove6=
PARAM_Remove8=
PARAM_Remove10=Remove
PARAM_Remove12=
PARAM_Remove14=

The user has in this case pressed the fifth button which represents line 10. The script is then called and the text string Remove is put into $PARAM_Remove10. So now I need to delete lines 10 and 11 from file /var/temp

How do I do this? To summerise, I want to do the following but with a better solution
Code:

if [ $PARAM_Remove2 = Remove ]: then
  sed '2,3d' /var/temp > /var/temp2 && mv /var/temp2 /var/temp
fi
if [ $PARAM_Remove4 = Remove ]: then
  sed '4,5d' /var/temp > /var/temp2 && mv /var/temp2 /var/temp
fi
if [ $PARAM_Remove6 = Remove ]: then
  sed '6,7d' /var/temp > /var/temp2 && mv /var/temp2 /var/temp
fi
if [ $PARAM_Remove8 = Remove ]: then
  sed '8,9d' /var/temp > /var/temp2 && mv /var/temp2 /var/temp
fi
if [ $PARAM_Remove10 = Remove ]: then
  sed '10,11' /var/temp > /var/temp2 && mv /var/temp2 /var/temp
fi
if [ $PARAM_Remove12 = Remove ]: then
  sed '12,13d' /var/temp > /var/temp2 && mv /var/temp2 /var/temp
fi
if [ $PARAM_Remove14 = Remove ]: then
  sed '14,15d' /var/temp > /var/temp2 && mv /var/temp2 /var/temp
fi
if [ $PARAM_Remove16 = Remove ]: then
  sed '16,17d' /var/temp > /var/temp2 && mv /var/temp2 /var/temp
fi

But that looks so messy and it is hard to tell how many lines the user will have in that file...

Hope this clears things up.


Thanks

unSpawn 10-31-2006 07:35 AM

Here's an idea. It's tested except for the sed part. It does use *Bash* though. Converting it to Bourne could be done (by you, not me). I wedged in some minimal checks and added TODO list items if you want to expand this. You should change FILE_NAME and FILE_TEMP_NAME locations and fill FILE_NAME with meaningful content for testing. If your webserver does not allow you to do file ops in the location you specify (good) then set up a Sudo Cmnd_Alias with NOPASSWD for it. No servicable user parts inside. Usability not implied nor functionality guaranteed. Store cold and dry. Do not drop. Keep away from children. Shell scripting addiction hazard warning.
YMMV(VM), as always ;-p

Code:

#!/bin/bash - 
# Purpose: Delete lines from FILE_NAME example.
# Args: none, expect QUERY_STRING
# Deps: Bash, GNU utils
# Run from: httpd as cgi
set -e
# Prepstage functions
PROGNAME=${0//*\//}
declare -r GOODCHARS="a-zA-Z0-9_&?="
FILE_NAME="/var/www/cgi-bin/test.dat"
FILE_TEMP_NAME="/var/www/cgi-bin/test.tmp"

# Access custom functions
function log() { # Log message, better not log QUERY_STRING
 /usr/bin/logger -i -p kern.err -t ${PROGNAME:="some unknown CGI"} "$@"
}

function checkChars() { # Check for allowed characters
 [ -n "${QUERY_STRING//[$GOODCHARS]/}" ] || \
 { echo "DENIED</title></head><body><h1>Illegal action.</h1>Try again.</body></html>"; exit 1; }
 # TODO: other checks should include:
 # - min length of QUERY_STRING
 # - max length of QUERY_STRING
 # - max elements in array QUERY_STRING
 # - existence of any LINENO= elements
 # - element format:
 #  - element[n]:0:6 equals "LINENO=",
 #  - only numeric after equal sign
 # - last element is "Action=Remove+lines"
}

function insertForm() { # insert form in page
 local i; echo "<form name=\"Remove\">"; LINES=($(wc -l "${FILE_NAME}"))
 LINES=${LINES[0]}; for i in $(seq 1 ${LINES}); do
  echo "Remove line ${i}: <input TYPE=\"checkbox\" NAME=\"LINENO\" VALUE=\"${i}\" unchecked><br>"
 done;  echo "<input TYPE=\"submit\" NAME=\"Action\" VALUE=\"Remove lines\"></form>"
}

function removeRoutine() { # Change to using at, sudo or whatever.
 # TODO: parse exit value and return an understandable message.
 echo "sed ${i//LINENO=/}d ${FILE_NAME} > ${FILE_TEMP_NAME} \
 && mv -f ${FILE_TEMP_NAME} ${FILE_NAME}"
}

function queryAction() { # parse out returned string
 # TODO: this should have the array elements reversed.
 for i in ${QUERY_STRING//&/ }; do case "${i}" in LINENO=*) removeRoutine;; *) ;; esac
done
}

[ ! -e "${FILE_NAME}" ] && exit 127
[ -f "${FILE_TEMP_NAME}" ] && rm -f "${FILE_TEMP_NAME}"

# Bare minimum HTML header
echo -en "Content-type: text/html\n\n<html><head><title>\n"
checkChars
echo "Remove lines</title></head><body><pre>"
#echo "QS: ${QUERY_STRING//&/ }"
queryAction
echo "</pre>"
insertForm
echo "</body></html>"

exit 0

Have fun!

unSpawn 10-31-2006 07:51 AM

Hmm. Come to think of it, you can slash the whole iteration thing if you can make queryAction return all and only numbers. Then you could make removeRoutine do sed '${number}d;${number}d;${number}d;' file.

Fredde87 10-31-2006 09:01 AM

Sorry you will have to excuse me, I am fairly new to this. Especially bash scripting, never written in bash, always sticked to bourne. I cant use any "${XXXX}" (If I understand that correctly what it does is check that the variable exists and that it is not null (unless it has semicolons in it).). Ill give it a play later today.


Edit: Got it to work! I had a hard time with getting your script to work though but your script pointed me in the right direction! Thank you very much! Switching to checkboxes and adding,
Code:

if [ $var = LINENO ]; then
      LINEN2=$(echo $value.$LINEN2)
      echo "PARAM_LINENO2='$LINEN2'"
    else
      echo "PARAM_$var='$value'"
    fi

to querystring2sh made my life very easy! So I now have the variable $PARAM_LINENO2 which contains the lines with a . as a delimiter, so it looks like this, "10.6.4.2."

Now I can easily do a for script to delete one line at a time. They are also reversed so that it starts the deletion at the back. Thank you so much for your help!

unSpawn 10-31-2006 10:39 AM

Especially bash scripting, never written in bash, always sticked to bourne.
To me it seems a rather rare and valueable quality...

I can't see why you need to go through all sort of hoops with the querystring2sh? If you do enough tests on QUERY_STRING to make sure it's valid and sanitise (sed) unwanted parts you almost have your sed command.


So I now have the variable $PARAM_LINENO2 which contains the lines with a . as a delimiter, so it looks like this, "10.6.4.2."
If you can make $PARAM_LINENO2 read "10d;6d;4d;2d;" instead you already have your sed command and you can ditch the for loop.

Anyway. Good to see you got it working.

Fredde87 11-01-2006 03:35 AM

Never thought about that, might try that. Clean up some code. Thank you very much!


All times are GMT -5. The time now is 07:09 PM.