How to get variable from other while loop? - ash

#!/bin/bash
read()
{
count=0
cat localfile |
while read line
do
FILE[$((count += 1))]="$line"
done
}
read
for((i=0;i<${#FILE[#]});i++)
do
echo ${FILE[i]}
done
The result of echo is whole blank. Is there any way to get the FILE array?

You posted this under ash, the Almquist shell, but you are using bash, the Bourne-Again SHell.
One problem is the pipe. When you run a pipe in bash, each side runs in its own sub-shell, and any variables are local to it. The correct mechanism is to redirect into the loop.
Another problem is that your function is called read, which is a shell-builtin (you use it!). A good idea is to use a naming convention, like an f_ prefix, so you don't get these name collisions.
Another issue you have is that the syntax of your second for loop is wrong in several ways. Here is a corrected version:
#!/bin/bash
f_read()
{
count=0
while read line
do
FILE[$((count += 1))]="$line"
done < localfile
}
f_read
for ((i=0;i<${#FILE[#]};i++))
do
echo ${FILE[i]}
done

Related

Bash case statement doesnt work with json string value using jq

I working on a function which extract the choosen track from a media container (mkv,mp4...etc). One of its major feature will be the "auto output file extension assigner".
the process will be the following...
step 1) when i give the script the number of the track, which i want to extract, it automatically inspect the source file with mediainfo and output the results in JSON format.
step 2) With JQ, i query the value of the "track" key from the selected track, and save it to the "mediaFormat" variable.
step 3) put this variable in a switch statement and compare with a predefined list of switches. If there is a match, then it will initialize the "mediaExtension" variable
with the appropriate value, which will be used as a extension of the ouput file.
For now i just want echo the "mediaExtension" variable, to see if it works. And it DIDN'T WORK.
The problem is step 1-2 works as expected, but somehow the switch statement (step 3) doesn't work. Only the (*) switch will be executed, which means it doesn't recognize the "AVC" switch.
#!/bin/bash
# INCLUDES
# mediainfo binary
PATH=/cygdrive/c/build_suite/local64/bin-video:$PATH;
# jq binary
PATH=/cygdrive/c/build_suite/local64/bin-global:$PATH;
# BASH SETTINGS
set -x;
# FUNCTION PARAMETER
function inspectExtension () {
mediaFormat=$(mediainfo "$1" --Output=JSON | jq ".media.track[$2].Format");
case $mediaFormat in
"AVC") mediaExtension="264";;
*) echo "ERROR";;
esac
set "$mediaExtension";
echo "$mediaExtension";
}
inspectExtension "test.mp4" "1";
read -p "Press enter to continue...";
And as you can see, in this script i activated tracing (set -x), and this is what i see in the console window (i use cygwin on windows 10).
+ inspectExtension test.mp4 1
++ mediainfo test.mp4 --Output=JSON
++ jq '.media.track[1].Format'
' mediaFormat='"AVC"
+ case $mediaFormat in
+ echo ERROR
ERROR
+ set ''
+ echo ''
+ read -p 'Press enter to continue...'
Press enter to continue...
Any ideas? Or is something what i miss here?
Thx for the help!
Maybe the only thing you miss is using the --raw-output option of jq like so:
mediaFormat=$(mediainfo "$1" --Output=JSON | jq --raw-output ".media.track[$2].Format");
Whenever you use jq to access some string values, it will be best to use the --raw-output option because it get's rid of the enclosing quotes.
Assuming you want mediaFormat to be a JSON value (i.e., assuming the invocation of jq is the way you have it), "AVC" in the case statement should be quoted:
'"AVC"' ) ...
In addition, it would probably be safer to quote the argument of case.

Bash read line from variables

Hi & thanks in advance.
I'm trying to update a column(version) on an MySQL table from a Bash script.
I've populated a variable with the version numbers, but it fails after applying the first version in the list.
CODE:
UP_VER=`seq ${DB_VER} ${LT_VER} | sed '1d'`
UP_DB=`echo "UPDATE client SET current_db_vers='${UP_VER}' WHERE client_name='${CLIENT}'" | ${MYSQL_ID}`
while read -r line
do
${UP_DB}
if [[ "${OUT}" -eq "0" ]]; then
echo "Database upgraded.."
else
echo "Failed to upgrade.."
exit 1
fi
done < "${UP_VER}"
Thanks
Hopefully solved... My $UP_VER is in a a row not a column.
You're misunderstanding what several shell constructs do:
var=`command` # This executes the command immediately, and stores
# its result (NOT the command itself) in the variable
... < "${UP_VER}" # Treats the contents of $UP_VER as a filename, and tries
# to use that file as input
if [[ "${OUT}" -eq "0" ]]; then # $OUT is not defined anywhere
... current_db_vers='${UP_VER}' ... # this sets current_db_vers to the entire
# list of versions at once
Also, in the shell it's best to use lowercase (or mixed-case) variable names to avoid conflicts with the variables that have special meanings (which are all uppercase).
To fix the first problem, my recommendation is don't try to store shell commands in variables, it doesn't work right. (See BashFAQ #50: I'm trying to put a command in a variable, but the complex cases always fail!.) Either use a function, or just write the command directly where it's going to be executed. In this case I'd vote for just putting it directly where it's going to be executed. BTW, you're making the same mistake with ${MYSQL_ID}, so I'd recommend fixing that as well.
For the second problem, you can use <<< "${UP_VER}" to feed a variable's contents as input (although this is a bashism, and not available in generic posix shells). But in this case I'd just use a for loop:
for ((ver=db_ver+1; ver<=lt_ver; ver++)); do
For the third problem, the simplest way to test the success of a command is to put it directly in the if:
if somecommand; then
echo "Database upgraded.."
else # ... etc
So, here's my take at a rewrite:
mysql_id() {
# appropriate function definition goes here...
}
for ((ver=db_ver+1; ver<=lt_ver; ver++)); do
if echo "UPDATE client SET current_db_vers='${ver}' WHERE client_name='${client}'" | mysql_id; then
echo "Database upgraded.."
else
echo "Failed to upgrade.."
exit 1
fi
done
... but I'm not sure I understand what it's supposed to do. It seems to be updating current_db_vers one number at a time until it reaches $ver_lt... but why not set it directly to $ver_lt in a single UPDATE?
try something like :
done <<< "${UP_VER}"

GNU make call function with multiple arguments and multiple commands

I am trying to write a GNU make call function (example below) which has multiple shell commands to execute, such that it can be called with different arguments.
shell_commands = $(shell echo $(1); ls -ltr $(2))
try:
$(call shell_commands,$(FILE1),$(FILE2))
1) Is above the correct way to write a call function with multiple commands? By using a semi-colon to separate them? To make it readable, I write my targets as shown below. Is there a similar way to write a call function?
shell_commands:
echo $(1)
ls -ltr $(2)
2) I get this error from make when I execute make -B try. It looks like it is trying to execute /home/user/file1. But why?
make: execvp: /home/user/file1: Permission denied
make: *** [try] Error 127
3) Is it possible to pass variable number of parameters to a call function? Like pass in just the second parameter and not the first one.
$(call shell_commands,,$(FILE2))
I tried google-ing, searching on SO, and looking on gnu.org but I did not get any solutions. Will appreciate any answers or pointers to any resources which document call function with multiple optional arguments and commands.
Question 1: No, this is not right. The shell make function should NEVER be used inside a recipe: the recipe is already running in the shell, so why would you run another shell? It's just confusing. Second, it's generally recommended to use && between multiple commands in a recipe, so that if the first command fails the entire command will immediately fail, rather than continuing on and perhaps succeeding. Of course, that is not always correct either, it depends on what you're trying to do.
Question 2: This happens because the shell make function is like backticks in the shell: it expands to the output printed by the shell command it runs. Your shell command that make runs is:
echo $(1); ls -ltr $(2)
(where, one assumes, $1 expands to /home/user/file1) which prints the string /home/user/file1. After the expansion, that string is substituted into the recipe and make tries to run that recipe, giving the error you see above.
You want this, most likely:
shell_commands = echo $(1) && ls -ltr $(2)
try:
$(call shell_commands,$(FILE1),$(FILE2))
Now the call expands to the actual text, not an invocation of make's shell function, and then that text is run as the recipe.
Question 3: Sure, just using empty parameters means that the variable $1 (in this case) expands to the empty string.

Shell Script call a function with a variable?

Hi I'm creating a shell script.
and an example code looks like
#!/bin/bash
test_func(){
{
echo "It works!"
}
funcion_name = "test_func"
I want to somehow be able to call test_func() using the variable "function_name"
I know that's possible in php using call_user_func($function_name) or by sying $function_name()
is this also possible in the shell scripting?
Huge appreciation for the help! :)
You want the bash built-in eval. From man bash:
eval [arg ...]
The args are read and concatenated together into a single command. This command is then read and executed by the shell, and its exit status is returned as the value of eval. If there are no
args, or only null arguments, eval returns 0.
You can also accomplish it with simple variable substitution, as in
#!/bin/bash
test_func() {
echo "It works!"
}
function_name="test_func"
$function_name
#!/bin/bash
test_func() {
echo "It works!"
}
function_name="test_func"
eval ${function_name}

How do you save and parse a command output in Expect?

I am half-way through writing an Expect script on a Linux server which is supposed to telnet to a router in order to collect some system information. So far my script can successfully make the connection, run a router command, disconnect and terminate.
The command displays a few lines which I need to parse, something I am not sure how to do in Expect. How can I save the output, grep a line, then a column from the line, and finally save the result in a file? If possible, I would like to use Expect entirely rather than a work-around (for example Expect embdded in Bash).
Thanks for your time.
jk04
Two tips for expect development:
autoexpect to lay out a framework for your automation
exp_internal 1 to show verbosely what expect is doing internally. This one is indispensable when you can't figure out why your regular expression isn't capturing what you expect.
basically, $expect_out(buffer) [1]. holds the output from last expect match to the current one. you can find your command output there.
and for the string manipulation, you can simply employ the tcl's built-in [2][3].
"How to access the result of a remote command in Expect" http://wiki.tcl.tk/2958
"regexp" http://wiki.tcl.tk/986
"string match" http://wiki.tcl.tk/4385
I've faced and solved a similar problem for interacting with bash. I believe the approach generalizes to any other interactive environment that provides no-op, fixed-string output.
Basically, I wrap the command with two fixed strings and then search for the pattern that includes these strings at the beginning and end, and save the content in between them. For example:
set var "";
expect $prompt { send "echo PSTART; $command; echo PEND;\r"; }
expect {
-re PSTART\r\n(.*)PEND\r\n$prompt { set var [ string trim $expect_out(1,string) ]; send "\r"; }
-re $prompt { set var "" ; send "\r"; }
timeout { send_user "TIMEOUT\n"; exit }
}
I suspect that this approach would work with a shell's comment characters as well, or a simple status command that returns a known output.
Then you can do whatever you need with the content of 'var'.