Re: BASH help please??
From: Chris F.A. Johnson (cfajohnson_at_gmail.com)
Date: 06/29/05
- Next message: Chris F.A. Johnson: "Re: Error: *: Parameter not set"
- Previous message: Bruce Barnett: "Re: AWK or otherway to convert transpose Columns to Rows"
- In reply to: HellZFury_at_gmail.com: "BASH help please??"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Wed, 29 Jun 2005 08:47:50 -0400
On 2005-06-29, HellZFury@gmail.com wrote:
> I just have a few questions... I've looked in the "Advanced
> Bash-Scripting Guide" as someone else had suggested. It has answered a
> lot of my questions. However, I still have a few that I can't seem to
> find the answer to. Anyone that could help me/answer a few of these
> questions would have my gratitude.
> Also, since this obviously isn't the right place to be asking these
> questions, can anyone either give me a link to a more appropriate
> listserv or (if they are BASH mavericks) offer to help me offlist??
> This way I don't have to bother everyone.
This is as good a place as any. Other people can learn from the
responses you get.
> 1. How can I get find -exec to operate properly on "{" inside of a
> replacement "thingy"? Example:
> find -d ./* -exec echo "{}" ${{}//"e"/"h"} \;
Put the commands you want executed in a separate script, e.g.:
#!/bin/sh
eval echo "\$1 \${$1//e/h}"
And call it with the -exec operand:
find -d ./* -exec /path/to/script {} \;
> 2. I believe the following is interpreting "09" to be a ASCII
> reference (not sure though). What would be a good workaround for
> this?:
> user@host:~$ file_prompt.sh ~/test/
> 1:example_file1 7:example_file15 13:example_file6
> 2:example_file10 8:example_file16 14:example_file7
> 3:example_file11 9:example_file2 15:example_file8
> 4:example_file12 10:example_file3 16:example_file9
> 5:example_file13 11:example_file4
> 6:example_file14 12:example_file5
> Choose: 09/Users/matt/my_bin/auxiliary_scripts/file_prompt.sh: line
> 64: 09: value too great for base (error token is "09")
> /Users/matt/my_bin/auxiliary_scripts/file_prompt.sh: line 1: 09: value
> too great for base (error token is "09")
The message tells you that 09 is too great for the base; numbers
beginning with 0 are interpreted as octal. If a number may have a
leading zero, remove it before using it in any numerical
expression:
echo $(( ${x#0} + 1 ))
> 3. What would be the best ways to implement a "timeout" period in read
> statements? I am aware of the parameter in read, but that simply exits
> without returning a value. If the user types a 1, for example, and
> then waits a second or so, I want it to automatically accept the 1.
If you use the timeout (-t N) option, it will return 1 if it times
out.
Perhaps you want instead to accept only a single character; for
that use the -n option:
read -n1 var
> 4. What would be the best way to protect against user input of special
> characters such as "tabs", letters, and symbols?
What do you mean by "protect"?
If you want to keep leading whitespace, set IFS to an empty
string:
IFS= read var
If you also want raw input (no interpreting of backslashes as
special), use -r:
IFS= read -r var
> 5. Can someone please just look at my code and answer the following 2
> questions?
If you want your code to be read, do not post via Google groups,
as the code gets mangled, and it is very time consuming to figure
out exactly what it is supposed to be. (Or else make sure all
your lines are short enough not to be wrapped.)
Second, post the question with the code it refers to.
Third, describe what the script is supposed to do, and give a
detailed description of its failure, including the exact inputs
you give it and any error messages it printed (cut and paste, do
not retype the messages).
> a. How come the following is happening?
> user@host:~$ Script_template.sh test "foo bar"
> center.sh: String length greater than width!
Since you coded the condition that causes the error message, you
know where the error occurred, and you can easily examine the
values at that point.
> b. Is there a better way to take in input from commands than "$(<
> /dev/stdin)"?
From the code, it's not clear what you are trying to do. The
command already has its stdin connected to a pipe.
> =============================================================================
> file_prompt.sh
> -----------------------------------------------------------------------------
> #!/bin/bash
> ARGS_min=1 # $work_dir
> ARGS_max=2 # $work_dir $prompt
> E_FILE_DOES_NOT_EXIST=85
> E_PERM_DENIED=87
> E_FILE_NOT_DIR=88
> E_NUM_ARGS=95
> E_INVALID_USER_INPUT=98
> E_USER_ABORT=105
> work_dir="$1" # directory to work in
> prompt="${2-"Choose"}" # prompt to show user
> #chosen_num # Num from prompt
> #chosen_file # Name of chosen file
>
> max_tries=3 # Max num of prompts if invalid file
> selected
>
> command ls "$work_dir" | grep -n -v 'randtonotmatch' | column >&2
> num_files=$(command ls $work_dir | grep -c -v 'randtonotmatch')
> digits=1
> while (( $num_files >= 10 ))
The portable forms are perfectly usable; you gain nothing by using
((...)) instead of:
while [ $num_files -ge 10 ]
> do
> ((digits += 1))
> ((num_files /= 10))
Use the portable (POSIX) form of arithmetic:
digits=$(( $digits + 1 ))
num_files=$(( $num_files / 10 ))
You could also use the following and still be POSIX conformant,
but you don't gain anything, and there are some otherwise POSIX
shells that do not support it:
: $((digits += 1)) $((num_files /= 10))
> done
> num_files=$(command ls "$work_dir" | grep -c -v 'randtonotmatch')
> tries_left=$((max_tries - 1))
> read -p "$prompt: " -n $digits chosen_num >&2
> while [[ ! $num_files -ge $((chosen_num)) && $chosen_num && $tries_left
> -ge 1 ]]
I'm not sure exactly what the logic is supposed to be, but it
should be not far from this:
while [ $num_files -lt $chosen_num ] &&
[ "$chosen_num" ] && [ $tries_left -ge 1 ]
> do
> echo -e "\nInvalid file selection! You have $tries_left tries
> left." >&2
> read -p "$prompt: " -n $digits chosen_num >&2
> ((tries_left -= 1))
tries_left=$(( $tries_left - 1))
> done
> if [[ $chosen_num -eq 0 && $chosen_num ]]
What exactly are you testing here?
> then
> echo -en "\n" >&2
> fi
> if [[ ! $chosen_num || $chosen_num -eq 0 ]]
> then
> echo "$(basename $0): ABORT!!!" >&2
There is no need for the basename command in any POSIX shell:
echo "${0##*/}: ABORT!!!" >&2
> exit $E_USER_ABORT
> fi
> chosen_file=$(command ls "$work_dir" | grep -n -v 'randtonotmatch' |
> grep ^$((chosen_num)): | cut -d ":" -f 2-)
Why $((chosen_num)) instead of $chosen_num?
> if [[ $tries_left -eq 0 && ! -e "$chosen_file" ]]
> then
> echo >&2
> echo -e "\n$(basename $0): You have repeatedly chosen an invalid
> file! This program will now quit." >&2
> exit $E_INVALID_USER_INPUT
> fi
> echo " - $chosen_file" >&2
> echo "$chosen_file" >&1
> =============================================================================
> script_template.sh (3)
> -----------------------------------------------------------------------------
> #!/bin/bash
> filename="$1" # filename for script
> description="$2" # script description
> bw=50 # width of header box
>
> #echo "$(center.sh $(split_string.sh "$description" 5) $bw '#' '#')" >>
> $filename
> echo "$(split_string.sh "$description" 5 | center.sh $(< /dev/stdin)
> $bw '#' '#')" >> $filename
> =============================================================================
> center.sh (3)
> -----------------------------------------------------------------------------
> #!/bin/bash
> E_WRONG_INPUT=97
> text="$1"
> width=$2
> prefix="$3"
> suffix="$4"
> whitespace=$((width-${#text}))
>
> if ((whitespace < 0))
> then
> echo "$(basename $0): String length greater than width!" > /dev/stderr
No need for basename. See above.
The usual syntax for sending to stderr is:
echo "....." >&2
> exit $E_WRONG_INPUT
> fi
> odd_offset=$((whitespace % 2))
That's the right way to do arithmetic.
> before="$(string_mult.sh ' ' $((whitespace / 2 - ${#prefix})) )"
> after="$(string_mult.sh ' ' $((whitespace / 2 + odd_offset -
> ${#suffix})) )"
> echo "$prefix$before$text$after$suffix"
It would be much easier with printf.
I use this function to centre a string:
centre() { ## centre string on N columns. USAGE: centre [-N] string [...]
case $1 in
-[0-9]*) c_cols=${1#-}
shift
;;
*) c_cols=${COLUMNS:-78} ;; ## Default width
esac
string="$*"
c_len=$(( ( $c_cols - ${#string} ) / 2 + ${#string}))
printf "%${c_len}.${c_len}s" "$*"
}
> =============================================================================
> string_mult.sh (3)
> -----------------------------------------------------------------------------
> #!/bin/bash
> text="$1"
> mult=$2
> ans=""
>
> while [ $mult -gt 0 ]
> do
> ans="$ans$text"
> mult=$((mult - 1))
> done
> echo "$ans"
> =============================================================================
> split_string.sh (3)
> -----------------------------------------------------------------------------
> #!/bin/bash
> string="$1"
> max_per_section=$2
>
> for word in "$string"
You are only giving the loop a single argument; presumably you
mean:
for word in $string
> do
> if (($((${#word} + ${#this_line})) < $max_per_section))
> then
> this_line="$this_line $word"
> else
> echo -n "$this_line" >&1
There's no need for >&1.
> this_line="$word"
> fi
> done
> echo "$this_line"
Consider using "fold -s" instead of a script; it may be faster (I
wrote something similar just yesterday, and found that the fold
command was as fast or faster).
> =============================================================================
I haven't gone over the entire logic of your script, but you
might do well to consider a loop like this:
while :
do
: do_something
if test something
then
break
fi
done
Some of the scripts would be better as functions, so that you can
avoid command substitution and forking new processes which can
slow down your script.
--
Chris F.A. Johnson <http://cfaj.freeshell.org>
==================================================================
Shell Scripting Recipes: A Problem-Solution Approach, 2005, Apress
<http://www.torfree.net/~chris/books/cfaj/ssr.html>
- Next message: Chris F.A. Johnson: "Re: Error: *: Parameter not set"
- Previous message: Bruce Barnett: "Re: AWK or otherway to convert transpose Columns to Rows"
- In reply to: HellZFury_at_gmail.com: "BASH help please??"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|