how to store the print in the variable while keep printing on the screen? - linux

I have a command that keeps printing on the screen. I have to collect its print to do something, while, I have to supervise the print.
How could I do that in bash?

Use tee:
VAR=$(my_cmd --args ... | tee /dev/stderr)
tee outputs to the screen and a file at the same time. But, here we specify stderr, which is another stream that goes to the screen. The variable captures stdout, and tee puts a copy of that output on stderr which goes to your screen.

You can duplicate your stdout:
exec 9>&1
variable=$(date |tee >(cat - >&9))
echo "Variable contains: $variable"
prints:
Thu Oct 2 21:21:52 CEST 2014 #normal output from the date
Variable contains: Thu Oct 2 21:21:52 CEST 2014 #the echo...

Related

Linux Command to display filename and its datetime [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about a specific programming problem, a software algorithm, or software tools primarily used by programmers. If you believe the question would be on-topic on another Stack Exchange site, you can leave a comment to explain where the question may be able to be answered.
Closed 3 years ago.
Improve this question
I am trying to list the files in a directory along with date and time.
I tried using the below two commands:
ls
getting output as below:
abc.txt testFile.txt
Then
ls -ltr
getting output as below:
-rw-r--r-- 1 xxxxxxx domain users 23 Aug 22 09:00 RCS
-rw-r--r-- 1 xxxxxxx domain users 0 Sep 12 06:09 testFile.txt
I expect an output as:
Aug 22 09:00 RCS
Sep 12 06:09 testFile.txt
You can use below command to get the output as you needed :
ls -l|awk '{print $6,$7,$8,$9}'
We are using -l as long listing which will give you most of the information and then we will use awk command to fetch what we want. You can tweak the print statement as per your requirement.
As pointed out by F Hauri, it would be better to use a process substitution with stat and read from there, e.g.
while read time file;do
printf "%(%b %e %T)T %s\n" $time "$file"
done < <(stat -c '%Z %n' *)
Example Use/Output
$ while read time file;do
printf "%(%b %e %T)T %s\n" $time "$file"
done < <(stat -c '%Z %n' *)
Apr 12 15:36:38 comboboxfocus.c
Apr 12 15:36:38 comboboxfocus2.c
Nov 3 17:43:51 createonclick.c
Feb 28 19:01:54 cw-drawinput.c
Nov 28 07:08:19 debug.c
Apr 12 15:36:38 debugsig.c
Jun 20 16:35:32 evboxstruct.c
Jun 20 16:35:32 evboxstruct2.c
Aug 23 08:50:38 ex00-window.c
Aug 23 08:50:38 ex01-hello.c
Aug 23 08:50:38 ex02-packing.c
Aug 23 08:50:38 ex03-builder.c
Feb 28 19:01:54 exampleappmain.c
Feb 28 19:01:54 exampleappwin_final.c
Feb 28 19:01:54 exampleappwin_wsrch.c
Dec 11 03:56:00 examplewindow.c
Dec 16 10:11:15 file_dialog_new.c
Dec 11 03:56:00 infobarex.c
Dec 7 14:03:20 poppler_page.c
Either will work, but awk will likely be quite a bit faster.
Why not use stat command ?
stat -c "%z %n" *
or you can use find command with its "-printf" switch to get this :
find -maxdepth 1 -type -f -printf '%t %f\n'
Similar output can be fetched using the find command.
Intro
After reading all this thread, I think rprakash's answer is the more accurate, as they point to two standard indicated command: stat and find -maxdepth....
Another way, using ls | sed
You could write:
ls -og|sed -re 's/\S*\s+\S+\s+\S+\s+//'
But as bash could be very efficient and because I prefer to be able to choose time ouput format (strftime) as I want:
Clean bash way to list date and files:
based on rprakash's answer stat command, with pretty formatting, as David C. Rankin' purpose, but without useless forks:
Short oneliner:
while read tm fl;do printf "%(%b %e %T)T %s\n" $tm "$fl";done< <(stat -c %Z\ %n *)
More readable:
while read time file;do
printf "%(%b %e %T)T %s\n" $time "$file"
done < <(
stat -c '%Z %n' *
)
And if you want sorted list, you could add sort in this way:
while read time file;do
printf "%(%b %e %T)T %s\n" $time "$file"
done < <(
stat -c '%Z %n' * |
sort -n
)
Some explanations:
From man stat:
%x time of last access, human-readable
%X time of last access, seconds since Epoch
%y time of last data modification, human-readable
%Y time of last data modification, seconds since Epoch
%z time of last status change, human-readable
%Z time of last status change, seconds since Epoch
From man bash
while read ... do; ... ; done < <(stat ... | sort ..)
Process Substitution
Process substitution allows a process's input or output to be referred to
using a filename. It takes the form of <(list) or >(list). The process list
is run asynchronously, and its input or output appears as a filename. This
filename is passed as an argument to the current command as the result of the
expansion. If the >(list) form is used, writing to the file will provide
input for list. If the <(list) form is used, the file passed as an argument
should be read to obtain the output of list. Process substitution is sup‐
ported on systems that support named pipes (FIFOs) or the /dev/fd method of
naming open files.
When available, process substitution is performed simultaneously with parame‐
ter and variable expansion, command substitution, and arithmetic expansion.
printf "%(...)T" $UNIXTIME
printf [-v var] format [arguments]
...
%(datefmt)T
causes printf to output the date-time string resulting from
using datefmt as a format string for strftime(3). The corre‐
sponding argument is an integer representing the number of sec‐
onds since the epoch. Two special argument values may be used:
-1 represents the current time, and -2 represents the time the
shell was invoked. If no argument is specified, conversion
behaves as if -1 had been given. This is an exception to the
usual printf behavior.

sed permission denied on temporary file

With sed I try to replace the value 0.1.233... On the command line there is no problem; however, when putting this command in a shell script, I get an error:
sed: couldn't open temporary file ../project/cas-dp-ap/sedwi3jVw: Permission denied
I don't understand where this temporary sedwi file comes from.
Do you have any idea why I have this temporary file and how I can pass it?
$(sed -i "s/$current_version/$version/" $PATHPROJET$CREATE_PACKAGE/Chart.yaml)
++ sed -i s/0.1.233/0.1.234/ ../project/cas-dp-ap/Chart.yaml
sed: couldn't open temporary file ../project/cas-dp-ap/sedwi3jVw: Permission denied
+ printf 'The version has been updated to : 0.1.234 \n\n \n\n'
The version has been updated to : 0.1.234
+ printf '***********************************'
sed -i is "in-place editing". However "in-place" isn't really. What happens is more like:
create a temporary file
run sed on original file and put changes into temporary file
delete original file
rename temporary file as original
For example, if we look at the inode of an edited file we can see that it is changed after sed has run:
$ echo hello > a
$ ln a b
$ ls -lai a b
19005916 -rw-rw-r-- 2 jhnc jhnc 6 Jan 31 12:25 a
19005916 -rw-rw-r-- 2 jhnc jhnc 6 Jan 31 12:25 b
$ sed -i 's/hello/goodbye/' a
$ ls -lai a b
19005942 -rw-rw-r-- 1 jhnc jhnc 8 Jan 31 12:25 a
19005916 -rw-rw-r-- 1 jhnc jhnc 6 Jan 31 12:25 b
$
This means that your script has to be able to create files in the folder where it is doing the "in-place" edit.
The proper syntax is identical on the command line and in a script. If you used $(...) at the prompt then you would have received the same error.
sed -i "s/$current_version/$version/" "$PATHPROJET$CREATE_PACKAGE/Chart.yaml"
(Notice also the quoting around the file name. Probably your private variables should use lower case.)
The syntax
$(command)
takes the output from command and tries to execute it as a command. Usually you would use this construct -- called a command substitution -- to interpolate the output of a command into a string, like
echo "Today is $(date)"
(though date +"Today is %c" is probably a better way to do that particular thing).

Fail to exclude words from grep command

For filtering errors in log files I have a command something like that
sudo grep -R --color=always -ri "err" *.log | grep -v "terry"
but the output isn't what I want. I still see lines like
mail.log:Mar 27 10:31:44 (removed) postfix/smtp[5449]: 4EB0822348: to=, relay=(removed), delay=6.6, delays=0.55/0.02/3.4/2.6, dsn=2.0.0, status=sent (250 OK id=1csFlH-00010k-6T)
Why is that line here when I have excluded "terry" from it?
Your "--color=always" is why you are still getting the the result. Remember that the pipe sends the stdout of one program to stdin of another. The output of your first grep command is outputting colors. In order to output color to the screen you have to send the color codes to the shell. In order for the shell to interpret these characters as colors it must use escape codes like this:
echo -e "This is \e[31mRed"
The word "Red" will be red when it is echo'ed. So grep is sending the escape characters to the second grep command. Go ahead and try it for yourself by redirecting your first grep command to a file and then examining the file.
grep -R --color=always -ri 'err' /tmp/log/syslog > /tmp/log/syslog2
Now open the file in a text editor (Don't cat the file out as you will just see the colors).
ar 26 10:30:59 zipmaster07 cinnamon-screensaver-dialog: pam_ecryptfs: seteuid ^[[01;31m^[[Kerr^[[m^[[Kor
Mar 26 14:27:19 zipmaster07 cinnamon-screensaver-dialog: pam_ecryptfs: seteuid ^[[01;31m^[[Kerr^[[m^[[Kor
t^[[01;31m^[[Kerr^[[m^[[Ky was here with an ^[[01;31m^[[Kerr^[[m^[[Kor.
mail.log:Mar 27 10:31:44 (removed) postfix/smtp[5449]: 4EB0822348: to=<t^[[01;31m^[[Kerr^[[m^[[Ky#(removed
The line "terry#...." is not terry anymore it is "t^[[01;31m^[[Kerr^...." and an inverted match of "terry" is not equal to "t^[[01;31m^[[Kerr^....", therefore grep includes it.
You need to remove the color option.
jschaeffer#zipmaster07 ~ $ grep -R -ri 'err' /tmp/log/sys2.log
pam_ecryptfs: seteuid err
pam_ecryptfs: seteuid err
terry was here with an error.
mail.log:Mar 27 10:31:44 (removed) postfix/smtp[5449]: 4EB0822348: to=<terry#(removed)>, relay=(removed), delay=6.6, delays=0.55/0.02/3.4/2.6, dsn=2.0.0, status=sent (250 OK id=1csFlH-00010k-6T)
Now with the second grep.
schaeffer#zipmaster07 ~ $ grep -R -ri 'err' /tmp/log/sys2.log | grep -v terry
pam_ecryptfs: seteuid err
pam_ecryptfs: seteuid err
Hopefully this all make sense.

Print variable containing command

Can somebody tell me how to print variable and not executing it in bash? I mean:
bash#bash $ cat script.sh
#!/bin/bash
echo $1
bash#bash $ ./script.sh "`date`"
Sat Sep 20 18:42:19 CEST 2014
I don't want to get:
Sat Sep 20 18:35:37 CEST 2014
I want to get output:
date
I'm interested in how to prevent executing sent commands to script.
It looks like you are trying to prevent code from being injected into your script. The problem with echo $1 is that the contents of $1 are being evaluated by the shell. In order to avoid that, you need to wrap $1 in double quotes in your script:
#!/bin/bash
echo "$1"
Testing it out:
$ ./script.sh '`date`'
`date`
The problem in your question is that you are using double quotes around "date", so the expansion has occurred before your script is run. You can use set -x to see the difference:
$ set -x
$ ./script '`date`'
+ ./script '`date`'
`date`
$ ./script "`date`"
++ date # date is being run
+ ./script 'Sat Sep 20 18:01:32 BST 2014' # result is passed to your script
Sat Sep 20 18:01:32 BST 2014
There is nothing you can do about this.
I think the following section of my original answer is still relevant, so I'll leave it in.
Different types of quotes in bash
Backticks (which you have used in your question) are an old-fashioned way of capturing the output of executing a command. The more modern syntax is $( ), so if you wanted to store the current date in a variable you could do d=$(date). Single quotes are used for literal strings, so echo '$d' would output $d, not the value of the variable. Variables inside double quotes are expanded, so echo "$d" would output the value of the variable $d. It is always a good idea to wrap your variables in double quotes to prevent word splitting and glob expansion.
Replace the backticks from var1 with single quotes:
var1='date'
var2="echo $var1"
echo $var2

How To Create A Shell Script In Unix That Prepends To A File?

I'm trying to create a shell script called sv that will prepend to a file, but the solution I'm using seems to only be good for one use, and them the temporary file is deleted. Is there a way I can make a shell script that will be go to use over and over?
Here's the questions:
"Suppose we wish to maintain a list of all the dates when we logged on to our UNIX system. It would be easy to do this by adding the following to the
.login file:
date >> logdates
Unfortunately, the latest date comes at the end of file logdates. I want it at the front; that is, the file should contain login dates from latest to earliest. Write a C shell script sv that will be used as follows:
date | sv logdates
(This way, the script is quite general, and I can use it for other cases when I want to add things to the front of a file.)"
Here's the script I've come up with:
"#!/bin/sh
cat - logdates /tmp/out && mv /tmp/out logdates"
This will work once, when I try again the system tells me that /tmp/out doesn't exist.
Does anyone have any suggestions?
Thank you!
Using sponge utility:
#!/bin/sh
cat - "$1" | sponge "$1"
Your shell script sv could contain the following, where newline is your data read from the | and $1 is the filename passed to sv:
#!/bin/sh
read newline
(echo "$newline"; cat $1) > tmp; mv tmp $1
And then you could use it like:
$ date > logdates
$ date >> logdates
$ cat logdates
Tue Mar 11 22:14:34 CDT 2014
Tue Mar 11 22:14:37 CDT 2014
$ date | ./sv logdates
$ cat logdates
Tue Mar 11 22:14:50 CDT 2014
Tue Mar 11 22:14:34 CDT 2014
Tue Mar 11 22:14:37 CDT 2014
This will only work for one-line appends though, as read is terminated by the newline (/n) character.
Simply use tac ( reverse of cat ) to output a file in reverse
#!/bin/sh
tac logdates > /tmp/out && mv /tmp/out logdates

Resources