Command redirection - linux

How can I redirect output of a Linux commands along with the command to a file?
Like ls -l >> test.txt will only redirect the output and not the command also to the file. I am a novice

Using script command you can log, which will save the entire terminal session until you exit the program, for example
Commands executed
akshay#db-3325:/tmp$ script test.log
Script started, file is test.log
akshay#db-3325:/tmp$ cal
March 2017
Su Mo Tu We Th Fr Sa
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
akshay#db-3325:/tmp$ uptime
23:43:16 up 4:31, 1 user, load average: 0.78, 0.67, 0.44
akshay#db-3325:/tmp$ w
23:43:18 up 4:31, 1 user, load average: 0.72, 0.66, 0.43
USER TTY FROM LOGIN# IDLE JCPU PCPU WHAT
akshay tty7 :0 19:12 4:31m 7:49 0.28s /sbin/upstart --user
akshay#db-3325:/tmp$ whoami
akshay
akshay#db-3325:/tmp$ echo 'something'
something
akshay#db-3325:/tmp$ exit
exit
Script done, file is test.log
This is what logged :
akshay#db-3325:/tmp$ cat test.log
Script started on Thursday 16 March 2017 11:43:06 PM IST
akshay#db-3325:/tmp$ cal
March 2017
Su Mo Tu We Th Fr Sa
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
akshay#db-3325:/tmp$ uptime
23:43:16 up 4:31, 1 user, load average: 0.78, 0.67, 0.44
akshay#db-3325:/tmp$ w
23:43:18 up 4:31, 1 user, load average: 0.72, 0.66, 0.43
USER TTY FROM LOGIN# IDLE JCPU PCPU WHAT
akshay tty7 :0 19:12 4:31m 7:49 0.28s /sbin/upstart --user
akshay#db-3325:/tmp$ whoami
akshay
akshay#db-3325:/tmp$ echo 'something'
something
akshay#db-3325:/tmp$ exit
exit
Script done on Thursday 16 March 2017 11:43:38 PM IST

If you don't want to use script utility there are some alternatives, using set -x bash feature:
$ exec 2>log; set -x; pwd >&2;set +x;exec 2>&1;cat log
+ pwd
/home/gv/Desktop/PythonTests
+ set +x
Or you can also use a custom function without tricky redirections:
$ function logme { a="$#"; echo $a >log ; "$#" >>log 2>&1;cat log;return; }
$ logme pwd
pwd
/home/gv/Desktop/PythonTests
PS: You can offcourse send the output to another file , i.e logme pwd >>general log
Finally, you can build a custom script like bellow, making use also of set -v (verbose):
exec 3>&2 2>log 4<log
set -vx
"$#" >&2
set +vx
cat <&4 >&1 # Optionally combine with |tail -n +2 |head -n -2
#The cat is used to display in command line what is logged inside the file.

Related

Command for finding first and last login details in the system for every Wednesday in bash?

I have been able to get all login logs in the system for Wednesday. But I am unable to get the first and last login of each day on which it is Wednesday.
Progress so far - `last | grep '^[^reboot].*Wed'
Through the above I am achieving this:-
cent pts/0 :0 Wed Mar 11 11:57 - 11:41 (19+23:44)
cent pts/0 :0 Wed Mar 4 11:10 - 11:57 (7+00:46)
cent :0 :0 Wed Mar 4 11:10 - 11:42 (27+00:31)
cent pts/0 :0 Wed Jan 22 11:27 - crash (4+10:10)
cent :0 :0 Wed Jan 22 11:27 - crash (4+10:10
cent pts/0 :0 Wed Jan 22 11:12 - 11:21 (00:09)
cent pts/0 :0 Wed Jan 8 11:22 - 11:54 (12+00:32)
cent :0 :0 Wed Jan 8 11:21 - down (14+00:00)
But the expected output should be this:-
cent pts/0 :0 Wed Mar 11 11:57 - 11:41 (19+23:44)
cent pts/0 :0 Wed Mar 4 11:10 - 11:57 (7+00:46)
cent :0 :0 Wed Mar 4 11:10 - 11:42 (27+00:31)
cent pts/0 :0 Wed Jan 22 11:27 - crash (4+10:10)
//Second entry omitted as we are supposed to only display first and last
//login each day
cent pts/0 :0 Wed Jan 22 11:12 - 11:21 (00:09)
cent pts/0 :0 Wed Jan 8 11:22 - 11:54 (12+00:32)
cent :0 :0 Wed Jan 8 11:21 - down (14+00:00)
You can do it with a simple awk script:
##script.awk
# at the end, display the results
END { for (i in last_login) {
print i":"
print "first: " first_full[i]
print "last: " last_full[i]
}
}
function add_entry(date, hour, full) {
# test if last_login login for given date has been set
if (last_login[date]) {
# if so, compare with given hour, which is the latest
if (last_login[date] < hour) {
#if new hour is the latest, record it and login info
last_login[date] = hour;
last_full[date] = full
}
} else {
# no previous login recorded for this date, record the login
last_login[date] = hour;
last_full[date] = full
}
# same logic in first login
if (first_login[date]) {
if (first_login[date] > hour) {
first_login[date] = hour;
first_full[date] = full;
}
} else {
first_login[date] = hour;
first_full[date] = full;
}
}
# if you have `Wed` in line, parse it
/Wed/ {
add_entry($5"/"$6, $7, $0)
}
You can now test it:
last | awk -f script.awk
Try this:
#! /bin/bash
set -eu
export LC_ALL=C
# Generate a list of all abbreviations of all months.
months=($(for d in 1970-{1..12}-1; do date -d $d +%b; done))
# Convert the list of months into a regular expression suitable for sed.
months_rx=$(tr ' ' '|' <<<"${months[#]}" | sed 's/|/\\|/g')
# Read `last`s output into a variable, because we need it more than once.
last="$(last -F)"
# Build a list of date patterns for all Wednesdays, when a login happened.
dates=$(sed -n 's/.*Wed \('"$months_rx"'\) \(..\) ..:..:.. \(....\) - .*/\1 \2 ..:..:.. \3/p' <<<"$last" | uniq)
# Filter `last`s output for each date pattern and print the first and last line.
while read d; do
sed -n "/$d - /p" <<<"$last" | sed -n '1p;$p'
done <<<"$dates"

Parsing cal output in POSIX compliant shell script by read command

I am trying to write a POSIX compliant script, which will print all months in specified year $3, that have day in $1 (for example Mo, Tu,...) on a same date as $2 (1,2,3,...).
Example:
Input: ./task1.sh Tu 5 2006
Output:
September 2006
Mo Tu We Th Fr Sa Su
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30
December 2006
Mo Tu We Th Fr Sa Su
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
I have written this script:
#!/bin/sh
year=$3
dayInMonth=$2
dayInWeek=$1
index=1
while expr $index '!=' 13 >/dev/null; do
cal -m $index $year| tail -n +2| while read Mo Tu We Th Fr Sa Su ; do
eval theDay='$'$dayInWeek
if [ "$theDay" = "$dayInMonth" ]; then
cal -m $index $year;
fi
done
index=$(expr $index + 1)
done
But there is a problem with reading of third line of cal output. In these lines numbers of days usually don't start at Mo place. How can I parse third line of cal output so the numbers in $Mo, $Tu, $We,... are always correct?
Update: You've added the requirement for a posix conform solution. date -d as used in my answer is not POSIX conform. I'll keep the answer for those who are using GNU/Linux.
Btw, the following command gives you posixly correct the day of week offset of Jan 5, 2006:
cal 01 2006 | awk -v d=5 'NR>2{for(i=1;i<NF;i++){if($i==d){print i;exit}}}'
You need to tinker a little shell script around that.
I would use the date command, like this:
#!/bin/bash
dayofweek="${1}"
day="${2}"
year="${3}"
for m in {01..12} ; do
date=$(LANG=C date -d "${year}-${m}-${day}" +'%a %B')
read d m <<< "${date}"
[ "${d}" = "${dayofweek}" ] && echo "${m}"
done
Results:
$ bash script.sh Thu 05 2006
January
October
It's easier to check dates with the command date.
for month in {1..12}; do
if [[ $(date -d $(printf "%s-%2.2d-%2.2d" "$year" "$month" "$day") "+%a") == "Tue" ]]; then
cal -m $month $year;
fi
done
The script loops over the 12 months and generate a date based on year and day. The date command outputs the day of the in a 3 letters format with +%a.
If you want the day of week in number format, use +%u and == 2 in the if statement.

Linux - Is it possible to store console output in a variable but with the same format as its present in console?

Here is my scenario:
well i use csh
1)
$ ls -l /corefiles/ | grep "root"
-rw-r----- 1 root root 0 Sep 22 2014 core.3.4.
-rwxr-x--- 1 root root 92 Sep 22 2014 ss.sh
2)
$ set textInfo=`ls -l /corefiles/ | grep "root"`
$ echo $textInfo
-rw-r----- 1 root root 0 Sep 22 2014 core.3.4. -rwxr-x--- 1 root root 92 Sep 22 2014 ss.sh
But I need echo $textInfo to give output like 1).
How can I achieve this? I do not want to redirect the content into a file.
I need to store console output in a variable but with the same format as present in the console.
I need a variable which has content as below:
$ echo $textInfo
-rw-r----- 1 root root 0 Sep 22 2014 core.3.4.
-rwxr-x--- 1 root root 92 Sep 22 2014 ss.sh
Use echo "$textInfo" instead of echo $textInfo. Otherwise the variable is expanded as part of the command line instead of as a string, so the newlines aren't preserved.
try this:
textInfo=$(ls -l /corefiles/ | grep "root")
then
echo "$textInfo"

Using variables with sed

I'm trying to delete a part of a file using sed in Linux (Ubuntu). Specifically, I want to delete the first lines of a log file until the first occurrence of the current system date (using the pattern '10 Jan 13').
So, I store the date in a variable
root#server:/# VAR_DATE=`date -R | cut -c6-11`
And after that, I use sed
root#server:/# cat log_file.txt | sed -n -e '/$VAR_DATE/,$p'
But it doesn't work. I've tried a lot of combinations with the same result:
root#server:/# cat log_file.txt | sed -n -e '/"$VAR_DATE"/,$p'
root#server:/# cat log_file.txt | sed -n -e '/"${VAR_DATE}"/,$p'
root#server:/# cat log_file.txt | sed -n -e "/$VAR_DATE/,$p"
What I'm doing wrong?
Use double quotes so the variable $vardate gets expanded by the shell and escape the last $ so it's not expanded by the shell sed -n "/$vardate/,\$p" file:
$ cat file
6 Jan 13
7 Jan 13
8 Jan 13
9 Jan 13
10 Jan 13
11 Jan 13
12 Jan 13
13 Jan 13
$ vardate="10 Jan 13"
$ sed -n "/$vardate/,\$p" file
10 Jan 13
11 Jan 13
12 Jan 13
13 Jan 13

Shell script can not pass file data to shell input

cal April 2012 | cat > t | cat < t | more
Why does it showing nothing? Why isn't it showing
April 2012
Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
| (anonymous pipe) connects stdout (1) of the first process with stdin (0) of the second. After redirecting the output to a file, there is no stdout left, so there's nothing to pipe. Also, cat | cat < file does not really make sense, it gets two inputs connected to stdin (at least with bash, redirection comes later and "wins": echo uiae | cat <somefile will output the content of somefile)
If you want to display output of a command and, at the same time, write it to the file, use the tee binary. It writes to a file, but still writes to stdout
cal April 2012 | tee t | more
cat t # content of the above `cal` command
Because that first cat > t sends all its output to a file called t, leaving no more for the pipeline.
If your intent is to send it to a file and through more to the terminal, just use:
cal April 2012 | tee t | more
This | cat < t construct is very strange and I'm not even sure if it would work. It's trying to connect two totally different things to the standard input of cat and certainly unnecessary.
this works for me if there's no existing file named t in the current directory. I'm using bash on Ubuntu Oneiric.
$ cal April 2012 | cat > t | cat < t | more
April 2012
Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
$ cal April 2012 | cat > t | cat < t | more
$ rm t
$ cal April 2012 | cat > t | cat < t | more
April 2012
Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30

Resources