How do I prevent long emails from becoming attachments? - linux

When a build finishes, the bash script calls:
nail -s "Build completed" $towhom < buildreport
When the buildreport is over 2,000 characters long, it arrives as an attachment.
Where can one set the threshold for the size of the body becoming an attachment?

Based on the comment from ottomeister, There are a lot of reasons why the mail would be autoconverted to an attachment by nail
Individual lines are too long (>950 characters)
There are control characters in the message
If the message is UTF-8, then it seems as though it gets properly parsed (but this is only based on code from on-line, which may not be what you're using)
You could probably run the content of the file through a filter - e.g. to remove all non-ascii characters:
tr -cd '\11\12\15\40-\176' <buildreport | nail -s "Build completed" $towhom
... but that will clobber all UTF-8 characters
If you want to get the log input to wrap at 1 number of characters, then you can use a perl one-liner like:
perl -e 'use Text::Wrap; print wrap("", " ", <STDIN>);' < buildreport | nail -s "Build completed" $towhom

Related

tr and while/until commands in shell script

What I am doing is to encode a character 13 places from its current location.
For example, if I input welcome, it should echo jrypbzr.
this is what I wrote:
read words
echo $words | tr '[A-Za-z]' '[????]' (Please ignore the ???? part.)
This successfully solved the encoding problem, however, I need to input multiple times and the code I wrote only read one time. Can someone tell me how to input multiple times?
Thanks!
First, have your input in a text file. Then
while read words
do
# here, do whatever you want with words
done < your-input-file.txt
Explanation: you feed contents of the input file to the while loop, which reads it line by line and stores in words.
If you want to use a delimiter other than newline, you can use:
while IFS=";" read words
and place within the IFS= " " whatever delimiter you like.

xmessage long text - wrap?

I want to use xmessage (it is the only available in the release I am using sorry) to display a message to the user. The message is a bit long and does not fit in one line. xmessage displays the message in one line up the point that is visible.
Is there any way to automatically wrap the text?
I tried the trick to
echo -e " message line 1 \n message line 2" | xmessage -file -
BUT because my message is coming from a variable and I want to also use it in my logger I do not want to use the "\n" in it. Do I have any chances?
You can pipe the text through fmt.

Is there any way to get the page numbers in a PDF of a search pattern?

I have a PDF named test.pdf and I need to search for text My name in that PDF.
By using this script, I can do the job:
pdftotext test.pdf - | grep 'My name'
Is there any way to get the page number up to the text "My name" in terminal itself?
If you just want the linear page number (as opposed to the number which appears on the page), then you can do it by counting form-feed characters while you search for your text. pdftotext puts a form-feed at the end of every page, so the number of form-feeds prior to your text is one less than the (linear) page number the text is on. (Or thereabouts. Sometimes PDF files are not what they seem.)
Something like the following should work:
pdftotext test.pdf - |
awk -vRS=$'\f' -vNAME="My name" \
'index($0,NAME){printf "%d: %s\n", NR, NAME;}'
The following slightly more complicated solution will prove useful if you want to scan for more than one pattern. Unlike the simple solution above, this one will give you one line per pattern match, even if the same pattern matches twice on the same page:
pdftotext test.pdf - |
grep -F -o -e $'\f' -e 'My name' |
awk 'BEGIN{page=1} /\f/{++page;next} 1{printf "%d: %s\n", page, $0;}'
You can add as many patterns as you like to the grep command (by adding another -e string argument). The -F causes it to match exact strings, but that's not essential; you could use -E and a regex. The awk script assumes that all of the matches will either be a form-feed or a string that was matched, which is what you will get with the -o option to grep.
If you are looking for phrases, you should be aware that they might have line breaks (or even page breaks) in the middle. There's not a lot you can do about page breaks, but the first (pure awk) solution will handle line breaks if you change the call to index to a regular expression search, and write the regular expression with [[:space::]]+ replacing every single space in the original phrase:
pdftotext test.pdf - |
awk -vRS=$'\f' \
'/My[[:space:]]+Name/{printf "%d: %s\n", NR, "My Name";}'
In theory, you could extract the visible page number (or "page label" as it is called), but many PDF files do not retain this metadata and you'd need a real PDF parser to extract it.

Bash - process backspace control character when redirecting output to file

I have to run a third-party program in background and capture its output to file. I'm doing this simply using the_program > output.txt. However, the coders of said program decided to be flashy and show processed lines in real-time, using \b characters to erase the previous value. So, one of the lines in output.txt ends up like Lines: 1(b)2(b)3(b)4(b)5, (b) being an unprintable character with ASCII code 08. I want that line to end up as Lines: 5.
I'm aware that I can write it as-is and post-process the file using AWK, but I wonder if it's possible to somehow process the control characters in-place, by using some kind of shell option or by piping some commands together, so that line would become Lines: 5 without having to run any additional commands after the program is done?
Edit:
Just a clarification: what I wrote here is a simplified version, actual line count processed by the program is a hundred thousands, so that string ends up quite long.
Thanks for your comments! I ended up piping the output of that program to AWK Script I linked in the question. I get a well-formed file in the end.
the_program | ./awk_crush.sh > output.txt
The only downside is that I get the output only once the program itself is finished, even though the initial output exceeds 5M and should be passed in the lesser chunks. I don't know the exact reason, perhaps AWK script waits for EOF on stdin. Either way, on more modern system I would use
stdbuf -oL the_program | ./awk_crush.sh > output.txt
to process the output line-by-line. I'm stuck on RHEL4 with expired support though, so I'm unable to use neither stdbuf nor unbuffer. I'll leave it as-is, it's fine too.
The contents of awk_crush.sh are based on this answer, except with ^H sequences (which are supposed to be ASCII 08 characters entered via VIM commands) replaced with escape sequence \b:
#!/usr/bin/awk -f
function crushify(data) {
while (data ~ /[^\b]\b/) {
gsub(/[^\b]\b/, "", data)
}
print data
}
crushify($0)
Basically, it replaces character before \b and \b itself with empty string, and repeats it while there are \b in the string - just what I needed. It doesn't care for other escape sequences though, but if it's necessary, there's a more complete SED solution by Thomas Dickey.
Pipe it to col -b, from util-linux:
the_program | col -b
Or, if the input is a file, not a program:
col -b < input > output
Mentioned in Unix & Linux: Evaluate large file with ^H and ^M characters.

While Read Stores Each User Entry without Spaces

I want to ask the user to enter a few lines of text, it can by anything and I want to store it as a variable that I can call later on. I don't want to create multiple read commands, just one that can hold multiple paragraphs if needed.
I tried this:
echo "Enter your your paragraph:"
read -d '' -n 1 message
while read -d '' -n 1 -t 2 c
do
message+=$c
done
echo ""
echo "$message"
the output is always put into one line of text without spaces or anything. It would look like this when I run the code and enter a few lines of code:
Enter your broadcast message (When done, wait 2 seconds):
This is supposed to be a sentence.
And so is this.
Thisissupposedtobeasentence.Andsoisthis.
It should output the two sentences on sperate lines and with spaces included.
Don't use read for this; requiring all typing to be done without any two-second pauses (and conversely, forcing a wait of two seconds to complete the input) is not very user-friendly. Instead, just read input directly from standard input, which for interactive use simply requires an EOF (Control-d) to finish the input.
c=$(</dev/stdin)
read uses the characters in $IFS as word delimiters. Change your read statement to:
IFS= read -r -d '' -n 1 -t 2 c

Resources