I am attempting to read the contents of an RFID card with a bash script using a RFID reader I got from sparkfun, however the most promising piece of code I have found for it is:
#!/bin/sh
while :
do
rfid=`screen /dev/cu.usbserial-A600JNHR 9600`
echo "RFID #: $rfid"
sleep 1
done
which does what it is supposed to, only it never exits screen, so the variable cannot be checked against a known tag to perform an action.
My question: what do I need to do to get the tag in a variable so that I can use it to check and perform an action? Is this piece of code going about it all wrong, or do I just need to add an extra line or two to process the data?
This may work for you
#!/bin/bash
while read -r rfid; do
echo "RFID #: $rfid"
sleep 1
done < /dev/cu.usbserial-A600JNHR
I got it thank you SiegeX I double checked and had used tty instead of cu for the code, here I had given the default that I had found and when I changed it I grabbed the tty.
Related
I want to be able to interactively get output from the terminal in a way similar to a hereDOC. Ie I want the user to be able to type multiple lines, then have that information passed into a file with all the formatting maintained. Something like this.
echo "Type your message below. To finish the letter type DONE by itself on a line"
file=mktmp
cat << DONE > $file
obviously this doesn't work, because the EOF is found before DONE. I thought about passing the user to something like VIM, but my less computer savy coworkers have a hard time with vim/emacs/nano.
You need to use an editor; standard input is just a stream of bytes, not an editor. However, you don't have to hard-code a specific editor. EDITOR is a standard environment variable meant to allow your script's caller to choose which editor is used.
: ${EDITOR:?Please set the environment variable EDITOR to the editor of your choice}
echo "Type your message below, then save and exit your editor."
"$EDITOR" "$file"
EDITOR is typically set by the user in their shell configuration file, but can be set on-demand when you run your script.
$ EDITOR=nano yourScript.sh
okay, so I came up with this, but please help me find something better or improve on it.
echo "Type your message below, to finish the letter press CTL+D"
mapfile message
file=`mktemp`
for x in `seq 0 ${#message[#]}`
do printf "${message[$x]}" >> $file
done
cat $file
I have a bash script that includes a line like this:
matches="`grep --no-filename $searchText $files`"
In other words, I am assigning the result of a grep to a variable.
I recently found that that line of code seems to have a vulnerability: if the grep finds too many results, it annoyingly simply freezes execution.
First, if anyone can confirm that excessive output (and exactly what constitutes excessive) is a known danger with command substitution, please provide a solid link for me. I web searched, and the closest reference that I could find is in this link:
"Do not set a variable to the contents of a long text file unless you have a very good reason for doing so."
That hints that there is a danger, but is very inadequate.
Second, is there a known best practice for coping with this?
The behavior that I really want is for excessive output in command substitution
to generate a nice human readable error message followed by an error exit code so that my script will terminate instead of freeze. (Note: I always run my scripts with "set -e" as one of the initial lines). Is there any way that I can get this behavior?
Currently, the only solution that I know of is a hack that sorta works just for my immediate case: I can limit the output from grep using its --max-count option.
Ideally, you shouldn't capture data of unknown length into memory at all; if you read it as you need it, then grep will wait until the content is ready to use.
That is:
while IFS= read -r match; do
echo "Found a match: $match"
# example: maybe we want to look at whether a match exists on the filesystem
[[ -e $match ]] && { echo "Got what we needed!" >&2; break; }
done < <(grep --no-filename "$searchText" "${files[#]}")
That way, grep only writes a line when read is ready to consume it (and will block instead of needing to continue to read input if it has more output already produced than can be stored in the relatively small pipe buffer) -- so the names you don't need don't even get generated in the first place, and there's no need to allocate memory or deal with them in any other way.
i spent the better part of the day looking for a solution to this problem and i think i am nearing the brink ... What i need to do in bash is: write 1 script that will periodicly read your inputs and write them into a file and second script that will periodicly print out the complete file BUT only when something new gets written in, meaning it will never write 2 same outputs 1 after another. 2 scripts need to comunicate by the means of a lock, meaning script 1 will lock a file so that script 2 cant print anything out of it, then script 1 will write something new into that file and unlock it ( and then script 2 can print updated file ).
The only hints we got was the usage of flock and lockfile - didnt get any hints on how to use them, exept that problem MUST be solved by flock or lockfile.
edit: When i said i was looking for a solution i ment i tried every single combination of flock with those flags and i just couldnt get it to work.
I will write pseudo code of what i want to do. A thing to note here is that this pseudocode is basicly the same as it is done in C .. its so simple, i dont know why everything has to be so complicated in bash.
script 1:
place a lock on file text.txt ( no one else can read it or write to it)
read input
place that input into file ( not deleting previous text )
remove lock on file text.txt
repeat
script 2:
print out complete text.txt ( but only if it is not locked, if it is locked obviously you cant)
repeat
And since script 2 is repeating all the time, it should print the complete text.txt ONLY when something new was writen to it.
I have about 100 other commands like flock that i have to learn in a very short time and i spent 1 day only for 1 of those commands. It would be kind of you to at least give me a hint. As for man page ...
I tried to do something like flock -x text.txt -c read > text.txt, tried every other combination also, but nothing works. It takes only 1 command, wont accept arguments. I dont even know why there is an option for command. I just want it to place a lock on file, write into it and then unlock it. In c it only takes flock("text.txt", ..).
Let's look at what this does:
flock -x text.txt -c read > text.txt
First, it opens test.txt for write (and truncates all contents) -- before doing anything else, including calling flock!
Second, it tells flock to get an exclusive lock on the file and run the command read.
However, read is a shell builtin, not an external command -- so it can't be called by a non-shell process at all, mooting any effect that it might otherwise have had.
Now, let's try using flock the way the man page suggests using it:
{
flock -x 3 # grab a lock on file descriptor #3
printf "Input to add to file: " # Prompt user
read -r new_input # Read input from user
printf '%s\n' "$new_input" >&3 # Write new content to the FD
} 3>>text.txt # do all this with FD 3 open to text.txt
...and, on the read end:
{
flock -s 3 # wait for a read lock
cat <&3 # read contents of the file from FD 3
} 3<text.txt # all of this with text.txt open to FD 3
You'll notice some differences from what you were trying before:
The file descriptor used to grab the lock is in append mode (when writing to the end), or in read mode (when reading), so you aren't overwriting the file before you even grab the lock.
We're running the read command (which, again, is a shell builtin, and so can only be run directly by the shell) by the shell directly, rather than telling the flock command to invoke it via the execve syscall (which is, again, impossible).
I want to copy a .bin file in a .img file using mcopy. For this, I can use mcopy -i image.img bin.bin ::. When using this, it will tell me: Long file name "bin.bin" already exists.
a)utorename A)utorename-all r)ename R)ename-all o)verwrite O)verwrite-all
s)kip S)kip-all q)uit (aArRoOsSq):. Due to the size and importance of stable files in this project, I just always want to put in O (small size and no importance, just so you know).
So I searched, and found this could be done by using the command: echo "O" | mcopy -i image.img bin.bin ::. Great. However, mcopy has a slight delay, due to which the echo does NOT enter O on the right time (too soon). I tried to use { sleep 2; echo "O"; } | mcopy -i image.img bin.bin ::, which helps nothing either.
So: How to actually echo text to a command after a delay, using bash?
(For the comments: adding -n to the mcopy command does neither work)
EDIT: There seemed to be some confusion about the purpose of the question, so I will try to clarify it. Point is, I have a problem and I want it solved. This could be done by using mcopy in an alternative way, as proposed in the comments already, OR by delaying the echo to the command (as is the question).
Even if my problem is solved in a way where the mcopy command is altered, that still would not answer the question. So please keep that in mind.
You're asking the wrong question, and you already know the answer to the question you're asking.
For the question "How to actually echo text to a command after a delay, using bash?", the answer is precisely:
{ sleep $DELAY; echo $TEXT; } | command
However, that should hardly ever be necessary. It provides the given text to command's standard input after the given delay, which may cause the command to wait a bit before proceeding with the read input. But there is (almost) never a case where the data needs to be delayed until the command is already waiting for it -- if the command is reading from standard input.
In the case of mtools, however, mcopy is not reading the clash code from standard input. Instead, it is reading it directly from /dev/tty, which is the terminal associated with the command. Redirecting standard input, which is what the bash pipe operator does, has no effect on /dev/tty. Consequently, the problem is not that you need to delay sending data to mcopy's standard input; the problem is that mcopy doesn't use standard input, and bash has no mechanism to hijack /dev/tty in order to fake user input.
So the other question might be "how to programmatically tell mcopy which clash option to use?", but apparently you know the answer to that one, too: use the -D command line option (which works with all relevant mtools utilities).
Finally, a more complicated question: "Is there some way to automate a utility which insists on reading input from /dev/tty?" Here, the answer is "yes" but the techniques are not so simple as just piping. The most common way is to use the expect utility, which allows you to spawn a subprocess whose /dev/tty is a pseudo-tty which expect can communicate with.
Using bash I want to read over a list of lines and ask the user if the script should process each line as it is read. Since both the lines and the user's response come from stdin how does one coordinate the file handles? After much searching and trial & error I came up with the example
exec 4<&0
seq 1 10 | while read number
do
read -u 4 -p "$number?" confirmation
echo "$number $confirmation"
done
Here we are using exec to reopen stdin on file handle 4, reading the sequence of numbers from the piped stdin, and getting the user's response on file handle 4. This seems like too much work. Is this the correct way of solving this problem? If not, what is the better way? Thanks.
You could just force read to take its input from the terminal, instead of the more abstract standard input:
while read number
do
< /dev/tty read -p "$number?" confirmation
echo "$number $confirmation"
done
The drawback is that you can't automate acceptance (by reading from a pipe connected to yes, for example).
Yes, using an additional file descriptor is a right way to solve this problem. Pipes can only connect one command's standard output (file descriptor 1) to another command's standard input (file descriptor 1). So when you're parsing the output of a command, if you need to obtain input from some other source, that other source has to be given by a file name or a file descriptor.
I would write this a little differently, making the redirection local to the loop, but it isn't a big deal:
seq 1 10 | while read number
do
read -u 4 -p "$number?" confirmation
echo "$number $confirmation"
done 4<&0
With a shell other than bash, in the absence of a -u option to read, you can use a redirection:
printf "%s? " "$number"; read confirmation <&4
You may be interested in other examples of using file descriptor reassignment.
Another method, as pointed out by chepner, is to read from a named file, namely /dev/tty, which is the terminal that the program is running in. This makes for a simpler script but has the drawback that you can't easily feed confirmation data to the script manually.
For your application, killmatching, two passes is totally the right way to go.
In the first pass you can read all the matching processes into an array. The number will be small (dozens typically, tens of thousands at most) so there are no efficiency issues. The code will look something like
set -A candidates
ps | grep | while read thing do candidates+=("$thing"); done
(Syntactic details may be wrong; my bash is rusty.)
The second pass will loop through the candidates array and do the interaction.
Also, if it's available on your platform, you might want to look into pgrep. It's not ideal, but it may save you a few forks, which cost more than all the array lookups in the world.