Unix how to store fuser command output to a variable - bash

I'm a newbie in scripting and I have this problem:
I want to use the fuser command to check if a file with that name is present. I want to save the output of the command and later use it to rename the file before loading.
I have tried the following:
#!/bin/bash
file_name='test'
echo 'file_name before: '$file_name
file_name=`fuser FILE_DIR/SD/Test_file_??????????????.dat`
echo 'file_name after: '$file_name'
However, the result of the above code is:
-bash-3.00$ script.sh
file_name before :test
FILE_DIR/SD/Test_file_20180823120345.dat
file_name after:
The output of the command is not getting stored in variable but getting displayed in screen and I can't figure out why!
What I want to do is to store Test_file_20180823120345.dat in the file_name variable, and then remove the timestamp from that and rename the file to Test_file_20180823.dat.
Then after loading the data in the staging table again rename the file to the old file name that we have received and then archive the file with its original name.

Your problems is a missing closing single quote in:
echo 'file_name before: '$file_name
(which is also why you had to unbalance the quotes in echo 'file_name after: '$file_name' on the last line to get the script to run)
Instead, simply double-quote the entire string for echo that will allow expansion of your variable file_name, e.g.
echo "file_name after: $file_name"
Further, avoid using backticks for command substitution, instead use the $(...) form, e.g.
file_name=$(fuser FILE_DIR/SD/Test_file_??????????????.dat)
Or re-written it could be:
#!/bin/bash
file_name='test'
echo "file_name before: $file_name"
file_name=$(fuser FILE_DIR/SD/Test_file_??????????????.dat)
echo "file_name after: $file_name"
(note: your matching with fuser will only match a pattern with 14 characters after Test_file_ followed by .dat -- which may be what you want, but it can probably be written in a cleaner way depending on the possibilities of ??????????????)
While fuser will provide an error, you may also want to get in the habit of validating that your command substitution returned a variable by testing whether file_name is empty after the call to fuser, e.g.
#!/bin/bash
file_name='test'
echo "file_name before: $file_name"
file_name=$(fuser FILE_DIR/SD/Test_file_??????????????.dat)
if [ -z "$file_name" ]; then
echo "error: no file matched pattern" >&2
else
echo "file_name after: $file_name"
fi
Look things over and let me know if you have further questions.

Related

printing output of command history 1 from shell script

Here's my problem, from console if I type the below,
var=`history 1`
echo $var
I get the desired output. But when I do the same inside a shell script, it is not showing any output. Also, for other commands like pwd, ls etc, the script shows the desired output without any issue.
As value of variable contains space, add quotes around it.
E.g.:
var='history 1'
echo $var
I believe all you need is this as follows:
1- Ask user for the number till which user need to print the history in script.
2- Run the script and take Input from user and get the output as follows:
cat get_history.ksh
echo "Enter the line number of history which you want to get.."
read number
if [[ $# -eq 0 ]]
then
echo "Usage of script: get_history.ksh number_of_lines"
exit
else
history "$number"
fi
Added logic where it will check arguments if number of arguments passed is 0 then it will exit from script then.
By default history is turned off in a script, therefore you need to turn it on:
set -o history
var=$(history 1)
echo "$var"
Note the preferred use of $( ) rather than the deprecated backticks.
However, this will only look at the history of the current process, that is this shell script, so it is fairly useless.

How to get values back from an external function that requires interactivity?

One of the routines I frequently use is a check for valid arguments passed when invoking scripts. Ideally, I'd like to make these, and other, similar, routines external functions that I could call from any script, for handling these more trivial processes. But, I'm having trouble retrieving the values I need from said function(s), without making the process more complicated.
I have tried using command substitution (e.g., echoing the output of the external function into a variable name local to the calling script), which seems to at least work with simpler functions. However, working with this file checking function, requires the read command in a loop, and, thus, user interactivity, which causes the script to hang when trying to resolve the variable that function call is stored in:
#!/bin/bash
# This is a simple function I want to call from other scripts.
exist(){
# If the first parameter passed is not a directory, then the input is
#+ invalid.
if [ ! -d "$1" ]; then
# Rename $1, so we can manipulate its value.
userDir="$1"
# Ask the user for new input while his input is invalid.
while [ ! -d "$userDir" ]; do
echo "\"$userDir\" does not exist."
echo "Enter the path to the directory: "
read userDir
# Convert any tildes in the variable b/c the shell didn't get to
#+ perform expansion.
userDir=`echo "$userDir" | sed "s|~|$HOME|"`
done
fi
}
exist "$1"
How can I retrieve the value of userDir in the calling script without adding (much) complexity?
You can have the exist function interact with the user over stderr and still capture the variable with command substitution. Let's take a simplified example:
exist() { read -u2 -p "Enter dir: " dir; echo "$dir"; }
The option -u2 tells read to use file descriptor 2 (stderr) for interacting with the user. This will continue to work even if stdout has been redirected via command substitution. The option -p "Enter dir: " allows read to set the prompt and capture the user input in one command.
As an example of how it works:
$ d=$(exist)
Enter dir: SomeDirectory
$ echo "$d"
SomeDirectory
Complete example
exist() {
local dir="$1"
while [ ! -d "$dir" ]; do
echo "'$dir' is not a directory." >&2
read -u2 -p "Enter the path to the directory: " dir
dir="${dir/\~/$HOME}"
done
echo "$dir"
}
As an example of this in use:
$ d=$(exist /asdf)
'/asdf' is not a directory.
Enter the path to the directory: /tmp
$ echo "new directory=$d"
new directory=/tmp
Notes:
There is no need for an if statement and a while loop. The while is sufficient on its own.
Single quotes can be put in double-quoted strings without escapes. So, if we write the error message as "'$dir' is not a directory.", escapes are not needed.
All shell variables should be double-quoted unless one wants them to be subject to word splitting and pathname expansion.
Right off the bat I'd say you can 'echo' to the user on stderr and echo your intended answer on stdout.
I had to rearrange a bit to get it working, but this is tested:
exist(){
# If the first parameter passed is not a directory, then the input is
#+ invalid.
userDir="$1"
if [ ! -d "$userDir" ]; then
# Ask the user for new input while his input is invalid.
while [ ! -d "$userDir" ]; do
>&2 echo "\"$userDir\" does not exist."
>&2 echo "Enter the path to the directory: "
read userDir
done
else
>&2 echo "'$1' is indeed a directory"
fi
echo "$userDir"
}
When I tested, I saved that to a file called exist.inc.func
Then I wrote another script that uses it like this:
#!/bin/sh
source ./exist.inc.func
#Should work with no input:
varInCallingProg=$(exist /root)
echo "Got back $varInCallingProg"
#Should work after you correct it interactively:
varInCallingProg2=$(exist /probablyNotAdirOnYourSystem )
echo "Got back $varInCallingProg2"

How do I tell if a variable is a file in bash

I am not a great bash scripter and hence have a few questions. One of which is how (or even whether) bash understands that a variable is a "file" or simply a local variable.
file=/usr/share/lib
Obviously this is a file to be saved, etc and can be used like so:
echo "$output" > $file
To save the output of $output to $file.
But where in bash does it calculate whether it's a file or not, is it only a file once it's been passed to a 'writing method'?
If you treat that variable as a file name, bash will simply do what it's told e.g.
echo "Test output" > $file
will work regardless of file being set to /tmp/myfile.txt, or to abcd. In the above you're using bash's file redirection to write out the standard out to the file you've named.
Consequently if you use the wrong variable in the above pattern, or have the value set incorrectly, bash will simply follow your instructions and you may end up with incorrectly named/located files.
You need to check this yourself, bash is only aware of the contents of the variable. If you want to check if a file location is held within a variable, you can test for it using the -f test operator
if [ -f "$file" ]
then
echo "$file is a file"
echo "$output" > "$file"
else
echo "$file is not a file"
fi
Variables are strings - nothing more, nothing less. (I'm ignoring array variables here, WLOG).
Bash (or any shell) expands variables blindly, without considering what you intend to do with them. It's only in the next stage of command processing that the contents of the string matter.
To use your example:
output="foo bar"
file=/usr/share/lib
echo "$output" >"$file"
(I've quoted $file even though it's not necessary here, simply because I've been bitten too many times by changing the value and breaking everything).
The line
echo "$output" >"$file"
gets transformed into
echo "foo bar" >"/usr/share/lib"
and only then does bash consider the > and attempt to open /usr/share/lib for writing.

bash backup script error

So I'm writing a simple backup script that when called will either back up a specific file or all the files in my current directory into my backup directory.
This is my script
#!/bin/bash
#verify if $1 is empty, if so, copy all content to backup directory
if [ -z "$1" ]
then
$files=ls
#Looping through files
for file in $files
do
cp $file ../backup/
done
#else copy files specified
else
$files=ls $1
#Looping through files
for file in $files
do
cp $file ../backup/
done
fi
and the only error I'm getting is:
./backup: line 7: =ls: command not found
I'm not sure why the script won't recognize ls as a command. Any ideas?
to assign a variable, you don't need the dollar sign:
files=foo
to save the output of an command to a var, you need do:
files=$(ls)
or
files=$(ls /path/to/some/dir)
I see two mistakes:
When you initialize variable "files" you should not prepend it with "$" symbol
Command ls should be placed in back quotes (`):
This is a short example:
#!/bin/bash
files=`ls`
for file in $files
do
echo "file: $file"
done
You should try putting the ls into ` marks - better, the back quote. Like this:
files=`ls`
Here a little background. Check this page for more information about quotation marks in the shell environment:
The back quote is not used for quoting characters. That character is
used for command substitution, where the characters between them are
executed by the shell and the results is inserted on that line.
Example:
echo the date is `date`

problems with checking for a directory in bash shell script

I am writing a batch-processing script bash that needs to first check to see if a folder exists to know whether or not to run a certain python script that will create and populate the folder. I have done similar things before that do fine with changing the directories and finding directories from a stored variable, but for some reason I am just missing something here.
Here is roughly how the script is working.
if [ -d "$net_output" ]
then
echo "directory exists"
else
echo "directory does not exist"
fi
when I run this script, I usually echo $net_output in the line before to see what it will evaluate to. When the script runs I get my else block of code saying "Directory does not exist", but when I then copy and paste the $net_output directory path that is echoed before into the shell terminal, it changes directories just fine, proving that the directory does in fact exist. I am using Ubuntu 12.04 on a Dell machine.
Thank you in advance for any help that someone can offer. Let me know what additional information I can provide.
The most common cases I've encountered when someone posts a problem like this are the following:
1. The variable contains literal quotes. Bash does not recursively parse quotes, it only parses the "outer" quotes given on the command line.
$ mkdir "/tmp/dir with spaces"
$ var='"/tmp/dir with spaces"'
$ echo "$var"
"/tmp/dir with spaces"
$ [ -d "/tmp/dir with spaces" ]; echo $?
0
$ [ -d "$var" ]; echo $? # equivalent to [ -d '"/tmp/dir with spaces"' ]
1
2. The variable contains a relative path, and the current directory is not what you expected. Check that the value of echo "$PWD" outputs what you expected.
3. The variable was read from a file with dos line endings, CRLF (\r\n). Unix and unix-like systems use just LF (\n) for line endings. If that's the case, the path will contain a CR (\r) at the end. A CR at the end of a line will be "invisible" in a terminal. Check with printf '%q\n' "$var" while debugging the script. See BashFAQ 52 on how to get rid of them.

Resources