How can I add formatting punctuation to user input as they type text using Bash's "read" utility prompt? - linux

I have the following lines to prompt a user to enter a date/time that I use further down in my script:
printf "Please insert start of time window in format YYYY-MM-DD HH:MM:SS: \n"
read startDatetime
Is there a way I can have the "read" prompt insert in the dashes for the date, the space between the date and time, and the colons for the time dynamically as a user types? I know this is possible using JavaScript on the web, but I am specifically looking for a command line version of that functionality.
I searched the web for an answer to this and cannot find one. I see an option for delimiter in the read Man Page but cannot see how it would achieve what I am trying to do.

You can cobble it together by limiting the number of characters you read at a time using the -N flag for read:
echo "Please enter start time in YYYY-MM-DD HH:MM:SS format"
read -rN4 year
printf '-'
read -rN2 month
printf '-'
read -rN2 day
printf ' '
read -rN2 hour
printf ':'
read -rN2 minute
printf ':'
read -rN2 second
echo
printf -v datetime '%s-%s-%s %s:%s:%s' \
"$year" "$month" "$day" "$hour" "$minute" "$second"
echo "You entered $datetime"
Looks like this:
Alternatively, with the option to move the cursor back past the automatically added -, blank, and :: we can use tput to move the cursor, clear the line, and then use read -e (use readline for input) and -i (prefill prompt):
moveup=$(tput cuu1)
clearln=$(tput el)
echo "Please enter start time in YYYY-MM-DD HH:MM:SS format"
read -rN4 datetime
printf '%b' '\r' "$clearln"
read -erN7 -i "$datetime-" datetime
printf '%b' '\r' "$moveup" "$clearln"
read -erN10 -i "$datetime-" datetime
printf '%b' '\r' "$moveup" "$clearln"
read -erN13 -i "$datetime " datetime
printf '%b' '\r' "$moveup" "$clearln"
read -erN16 -i "$datetime:" datetime
printf '%b' '\r' "$moveup" "$clearln"
read -erN19 -i "$datetime:" datetime
echo "You entered $datetime"
This isn't perfect, either: the extra characters are only inserted once, but not when you go back to fix something; and there's a newline inserted after the prompt that results in a blank line, which would be noticeable at the bottom of the terminal emulator:
At this point, you might consider using something like charmbracelet/gum for a very user-friendly interactive input experience at the terminal.

No, it is not possible. But note that read offers the -p switch:
read -p "Please insert start of time window in format YYYY-MM-DD HH:MM:SS: \n" startDatetime

Related

shell script for extracting specfic date log

I have to extract particular date's log from any application log. what we generally do
vi app.log
/date (searching)
.= (position where date found)
G(last line)
first_line,Last_line w filname.log (to generate log file with particular date)
I made small script but I think My logic is not correct:
#!/bin/bash
read -p " enter the App name" : app
read =p " enter the Date in this Format 10 Jan 20:01:00" : date
FILE=/logs/$app.log
# Check if $app exists or not
if test ! -f "$app" /logs
then
echo "Error - $app not found."
else
First_line=$(cat $FILE | grep $date | .=)
Second_line=$(tail -1 $FILE | .=)
vi $FILE | $First_line, $Second_line w $app.txt
fi
There is an error in the line
if test ! -f "$app" /logs
You seperated "$app" and /logs with a space; test -f expects one parameter, so it should be:
if test ! -f "$app"/logs
To get the log starting from the date given to the current end of the file, You should use sed (meaning "stream editor"), which is designed for noninteractive editing of files:
sed -n '/'$date'/,$ p' $app.log >$app.txt
The parameter -n is to only output, what matched and not the whole file.
Here I am telling sed to extract everything between two regular expressions, the first one, being the $date parameter given, the second one meaning "end of file". You could change this easily to extract all logs for one specific day or whatever.
As something seems to be unclear I am adding a complete example:
I have an apache log file, where the date logged looks like this [01/Apr/2015:22:31:21 +0200].
So I can do:
export date="01\/Apr\/2015"
It is important to note that I am escaping the slashes here, as they are going into a regex. This has to be done with everything that has some special meaning inside regexes too.
Then I can do:
sed -n '/'$date'/,$ p' myaccess.log
and I get everything logged for the 1st of April until the end of the file.
You could pass commands to vi from your script with a here document, if your task is as mechanical as you describe.
I'm talking about something like this
#!/bin/bash
read -p " enter the App name" : app
read =p " enter the Date in this Format 10 Jan 20:01:00" : date
FILE=/logs/$app.log
# Check if $app exists or not
if test ! -f "$app" /logs
then
echo "Error - $app not found."
else
vi $FILE <<-_EOF_
/date
.=
G
first_line,Last_line w filname.log
etc, etc
_EOF_
fi
I just wrote this as an idea that you mighr find fun to explore. I know the code will not work.
I'd use awk:
awk -v date="$date" '$0 ~ date {p=1} p' file.in > file.out
That will start printing lines when the date is first seen, until the end of the line.

time format change in shell

In my log file the time format is 2014-10-10 HH:MM:SS:sss.
I am reading the time and date from the file and converting it into seconds for further processing. But it's giving error the date is invalid. May be it's because time is not in ..SS.sss format.
while read line;
do
d1=$(echo $line | cut -d, -f2);
case "$line" in \s*) continue ;; esac
t1=$(echo $line | cut -d, -f3);
d1t1="${d1} ${t1}";
echo "$d1t1";
ds1=$(date -d"$d1t1" "+%s");
echo "$ds1";
done < error.log
I want to replace ":" by "." which is the way to solve the problem?
Why not to simply replace the milliseconds separator in your log file?
perl -p -i -e 's/:(\d{3}),/.\1/g' error.log
A ':' separator for milliseconds is wrong, and date command will complain aboult it. With a '.' separator date command will succeed.
UPDATE:
The command is a "perl inliner": it applies text changes to a file ("error.log", in this case), globally (for every row).
The change command here is a regular expression substitution: s/:(\d{3}),/.\1/g, which means: "substitute every colon followed by exaclty 3 digits and a comma with a dot and the digits".

How can I create a subdirectory that is a datetime stamp of the format YYYMMDDHHMMSS?

How can I create a subdirectory using BASH shell that is a datetime stamp of the format YYYMMDDHHMMSS? I am using mkdir ~/$(printf "%s" `date +"%Y%m%d%H%M%S"`) but keep getting unprintable characters ('?') on the end. Linux 2.6.18
You're don't need the printf to strip off newlines, $() will do that for you (see bash manual)
mkdir ~/$(date +"%Y%m%d%H%M%S")
To see what those unprintable chars are (you may have a trailing carriage return), try this:
for dir in 2013*; do printf "%s" "$dir" | od -c; done
What about using :
DIR_NAME="/Your_Path/"mydir`date +"%Y%m%d%H%M%S"`
or
printf `date +"%Y%m%d%H%M%S"`// Displaying 20130429173838

Bash script variable interpretation

I have a text file that contains references to variables and lets a user set up the formatting they want around variables, say something like
The date is $DATE
The time is $TIME
I then want to read this text file in, replace the variables, and print the result to stdout using a bash script. The closest thing I've gotten is using "echo" to output it,
DATE="1/1/2010"
TIME="12:00"
TMP=`cat file.txt`
echo $TMP
However, the output ends up all on one line, and I don't want to have \n at the end of every line in the text file. I tried using "cat << $TMP", but then there are no newlines and the variables inside the text aren't getting replaced with values.
You can use eval to ensure that variables are expanded in your data file:
DATE="1/1/2010"
TIME="12:00"
while read line
do
eval echo ${line}
done < file.txt
Note that this allows the user with control over the file content to execute arbitrary commands.
If the input file is outside your control, a much safer solution would be:
DATE="1/1/2010"
TIME="12:00"
sed -e "s#\$DATE#$DATE#" -e "s#\$TIME#$TIME#" < file.txt
It assumes that neither $DATE nor $TIME contain the # character and that no other variables should be expanded.
Slightly more compact than Adam's response:
DATE="1/1/2010"
TIME="12:00"
TMP=`cat file.txt`
eval echo \""$TMP"\"
The downside to all of this is that you end up squashing quotes. Better is to use a real template. I'm not sure how to do that in shell, so I'd use another language if at all possible. The plus side to templating is that you can eliminate that "arbitrary command" hole that Adam mentions, without writing code quite as ugly as sed.
Just quote your variable to preserve newline characters.
DATE="1/1/2010"
TIME="12:00"
TMP=$(cat file.txt)
echo "$TMP"
For your modification of values in file with variables you can do -
while read -r line
do
sed -e "s#\$DATE#$DATE#" -e "s#\$TIME#$TIME#" <<< "$line"
done < file.txt
Test:
[jaypal:~/Temp] cat file.txt
The date is $DATE
The time is $TIME
[jaypal:~/Temp] DATE="1/1/2010"
[jaypal:~/Temp] TIME="12:00"
[jaypal:~/Temp] while read -r line; do sed -e "s#\$DATE#$DATE#" -e "s#\$TIME#$TIME#" <<< "$line"; done < file.txt
The date is 1/1/2010
The time is 12:00

How can I get position of cursor in terminal?

I know I may save position using tput sc, but how can I read it's position to the variable? I need the number of row. I don't want to use curses/ncurses.
At ANSI compatible terminals, printing the sequence ESC[6n will report the cursor position to the application as (as though typed at the keyboard) ESC[n;mR, where n is the row and m is the column.
Example:
~$ echo -e "\033[6n"
EDITED:
You should make sure you are reading the keyboard input. The terminal will "type" just the ESC[n;mR sequence (no ENTER key). In bash you can use something like:
echo -ne "\033[6n" # ask the terminal for the position
read -s -d\[ garbage # discard the first part of the response
read -s -d R foo # store the position in bash variable 'foo'
echo -n "Current position: "
echo "$foo" # print the position
Explanation: the -d R (delimiter) argument will make read stop at the char R instead of the default record delimiter (ENTER). This will store ESC[n;m in $foo. The cut is using [ as delimiter and picking the second field, letting n;m (row;column).
I don't know about other shells. Your best shot is some oneliner in Perl, Python or something. In Perl you can start with the following (untested) snippet:
~$ perl -e '$/ = "R";' -e 'print "\033[6n";my $x=<STDIN>;my($n, $m)=$x=~m/(\d+)\;(\d+)/;print "Current position: $m, $n\n";'
For example, if you enter:
~$ echo -e "z033[6n"; cat > foo.txt
Press [ENTER] a couple times and then [CRTL]+[D]. Then try:
~$ cat -v foo.txt
^[[47;1R
The n and m values are 47 and 1. Check the wikipedia article on ANSI escape codes for more information.
Before the Internet, in the golden days of the BBS, old farts like me had a lot of fun with these codes.

Resources