csh idioms to check for environment variable existence? - environment-variables

I've got a few csh scripts where I need to check that certain environment variables are set before I start doing stuff, so I do this sort of thing:
if ! $?STATE then
echo "Need to set STATE"
exit 1
endif
if ! $?DEST then
echo "Need to set DEST"
exit 1
endif
which is a lot of typing. Is there a more elegant idiom for checking whether or not an environment variable is already set?
Notes:
This question is quite similar, but specifically asks about solutions in bash.
I'm not looking for people to advise me to stay away from csh because it's cursed, scary, or bash is better. I'm specifically interested in a more elegant solution than what I'm using now.

I think the way you're doing it (an if statement with a condition using the $?VAR syntax, which evaluates to 1 if the variable is set, and 0 otherwise) is probably the most idiomatic csh construct that does what you want.

Try the following:
[ -z STATE ] && echo "Need to set STATE"
[ ! -z DEST ] && echo "Need to set STATE"

Related

Is there a concise way to substitute return value of a function?

I'm putting together some unit tests for a dash shell script I've inherited and, given the large number of tests we want to add, I want to keep them as concise as possible.
To do this, I've added a check helper function to handle pass or failure of a test. It basically receives the return code $? from the previous command and passes or fails the test based on that. This allows me to simply implement a one-liner test (of which there are many) as:
for td in 1.1.1.999 {{lots of other invalid IPs}} ; do
isInvalidIpV4 ${td} ; check is-true $? "Checking ${td} is invalid"
done
for seg in 0 1 9 10 99 100 254 255 ; do
td="${seg}.${seg}.${seg}.${seg}"
isInvalidIpV4 ${td} ; check is-false $? "Checking ${td} is valid"
done
Now this is reasonably good since it gets each test down to a single line but, in my OCD quest for perfection, I wonder if there's even more scope for reduction.
I know that, like bash, there is the capability to run a command and use its output in another command, along the lines of:
echo "Lowercase name is $(echo PaxDiablo | tr '[A-Z]' '[a-z]')"
but I was wondering if there were a way to do this same substitution with the return code of a command.
That would mean my test line would be able to have that annoyingly repeated $? removed, changing from:
isInvalidIpV4 ${td} ; check pass $? "${td} should be invalid"
to something like:
check pass $[isInvalidIpV4 ${td}] "${td} should be invalid"
where $[] is the mythical "use return code" substitution operator.
Does dash have this facility or is there a more concise way to do it than what I've currently done?
Just keep in mind, other shells are not an option here, this is an embedded system for which dash is the only shell provided.
if ! isInvalidIpV4 $td
then
echo $td should be invalid
fi

How to get the return value of instruments?

I'm using instruments inside a bash script on a continuous integration server.
I would like to know when a command as failed in the script, so I can exit prematurely from it and mark the build as failed.
instruments displays a LOG ERROR to the console, but I don't succeed in getting a return value.
How can I achieve this?
if have:
instruments -w "iPhone 6 (8.3 Simulator)" -t
it is possible to do something like:
if(...)
then ...
Thanks in advance
Fairly easy:
instruments -w "iPhone 6 (8.3 Simulator)" -t
if (( $? > 0 ))
then
echo "instruments commands failed with error: $?" >&2
fi
The (( )) notation is for an arithmetic comparison. String pattern comparisons are done using [[ ... ]]. Be careful to use the correct spacing, in general whitespace is used as a separator in shells, so it can be significant.
An alternative syntax could be:
if instruments -w "iPhone 6 (8.3 Simulator)" -t
then
echo "it worked"
else
echo "it failed"
fi
and that is often preferable. But I think in this case the style I show fits better with what you need.
The special variable ? gives the return value of the previous command. Prefixing with a $ gives us the variable's value. By convention, a return value of zero means success, 1-255 means an error (the range on UNIX/Linux is 0-255, one byte). The significance of each error number is application specific, so you must read the documentation to find what it means.
Remember that $? gives us the return value of the previous command, so even an echo would reset it!
The >&2 means "send the output to the standard error stream". Error messages should go here, which is also known s stderr, file descriptor 2. It is a nice thing to do if you are redirecting output from the script.
EDIT: After all that, Apple does not appear to document an exit code for the instruments command, I checked the man page. That's poor in my opinion, but there's not much you can do. In languages like C (and probably ObjC) if the program ends without setting an exit code you just get any old value that is lying about in memory. So you cannot even rely on zero being success - unless you know otherwise?

Search for combinations of a phrase

What is the way to use 'grep' to search for combinations of a pattern in a text file?
Say, for instance I am looking for "by the way" and possible other combinations like "way by the" and "the way by"
Thanks.
Awk is the tool for this, not grep. On one line:
awk '/by/ && /the/ && /way/' file
Across the whole file:
gawk -v RS='\0' '/by/ && /the/ && /way/' file
Note that this is searching for the 3 words, not searching for combinations of those 3 words with spaces between them. Is that what you want?
Provide more details including sample input and expected output if you want more help.
The simplest approach is probably by using regexps. But this is also slightly wrong:
egrep '([ ]*(by|the|way)\>){3}'
What this does is to match on the group of your three words, taking spaces in front of the words
with it (if any) and forcing it to be a complete word (hence the \> at the end) and matching the string if any of the words in the group occurs three times.
Example of running it:
$ echo -e "the the the\nby the\nby the way\nby the may\nthe way by\nby the thermo\nbypass the thermo" | egrep '([ ]*(by|the|way)\>){3}'
the the the
by the way
the way by
As already said, this procudes a 'false' positive for the the the but if you can live with that, I'd recommend doing it this way.

What tools deal with spaces in columnar data well?

Let's start with an example that I ran into recently:
C:\>net user
User accounts for \\SOMESYSTEM
-------------------------------------------------------------------------------
ASPNET user1 AnotherUser123
Guest IUSR_SOMESYSTEM IWAM_SOMESYSTEM
SUPPORT_12345678 test userrrrrrrrrrrr test_userrrrrrrrrrrr
The command completed successfully.
In the third row, second column there is a login with a space. This causes many of the tools that separate fields based on white space to treat this field as two fields.
How would you deal with data formatted this way using today's tools?
Here is an example in pure** Windows batch language on the command prompt that I would like to have replicated in other modern cross-platform text processing tool sets:
C:\>cmd /v:on
Microsoft Windows [Version 5.2.3790]
(C) Copyright 1985-2003 Microsoft Corp.
C:\>echo off
for /f "skip=4 tokens=*" %g in ('net user ^| findstr /v /c:"The command completed successfully."') do (
More? set record=%g
More? echo !record:~0,20!
More? echo !record:~25,20!
More? echo !record:~50,20!
More? )
ASPNET
user1
AnotherUser123
Guest
IUSR_SOMESYSTEM
IWAM_SOMESYSTEM
SUPPORT_12345678
test userrrrrrrrrrrr
test_userrrrrrrrrrrr
echo on
C:\>
** Using variable delayed expansion (cmd /v:on or setlocal enabledelayedexpansion in a batch file), the for /f command output parser, and variable substring syntax... none of which are well documented except for at the wonderful website http://ss64.com/nt/syntax.html
Looking into AWK, I didn't see a way to deal with the 'test userrrrrrrrrrrr' login field without using substr() in a similar method to the variable substring syntax above. Is there another language that makes text wrangling easy and is not write-only like sed?
PowerShell:
Native user list example, no text matching needed
Get-WmiObject Win32_UserAccount | Format-Table -Property Caption -HideTableHeaders
Or, if you want to use "NET USER":
$out = net user # Send stdout to $out
$out = $out[4..($out.Length-3)] # Skip header/tail
[regex]::split($out, "\s{2}") | where { $_.Length -ne 0 }
# Split on double-space and skip empty lines
Just do a direct query for user accounts, using vbscript (or powershell if your system supports)
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * from Win32_UserAccount",,48)
For Each objItem in colItems
Wscript.Echo objItem.Name
Next
This will show you a list of users, one per line. If your objective is just to show user names, there is no need to use other tools to process thee data.
Awk isn't so great for that problem because awk is focused on lines as records with a recognizable field separator, while the example file uses fixed-width fields. You could, e.g., try to use a regular expression for the field separator, but that can go wrong. The right way would be to use that fixed width to clean the file up into something easier to work with; awk can do this, but it is inelegant.
Essentially, the example is difficult because it doesn't follow any clear rules. The best approach is a quite general one: write data to files in a well-defined format with a library function, read files by using a complementary library function. Specific language doesn't matter so much with this strategy. Not that that helps when you already have a file like the example.
TEST
printf "
User accounts for \\SOMESYSTEM
-------------------------------------------------------------------------------
ASPNET user1 AnotherUser123
Guest IUSR_SOMESYSTEM IWAM_SOMESYSTEM
SUPPORT_12345678 test userrrrrrrrrrrr test_userrrrrrrrrrrr
The command completed successfully.
\n" | awk 'BEGIN{
colWidth=25
}
/-----/ {next}
/^[[:space:]]*$/{next}
/^User accounts/{next}
/^The command completed/{next}
{
col1=substr($0,1,colWidth)
col2=substr($0,1+colWidth,colWidth)
col3=substr($0,1+(colWidth*2),colWidth)
printf("%s\n%s\n%s\n", col1, col2, col3)
}'
There's probably a better way than the 1+(colWidth*2) but I'm out of time for right now.
If you try to execute code as is, you'll have to remove the leading spaces at the front of each line in the printf statement.
I hope this helps.
For this part:
set record=%g
More? echo !record:~0,20!
More? echo !record:~25,20!
More? echo !record:~50,20!
I would use:
for /f "tokens=1-26 delims= " %a in (%g%) do (
if not "%a" = "" echo %a
if not "%b" = "" echo %b
if not "%c" = "" echo %c
rem ... and so on...
if not "%y" = "" echo %y
if not "%z" = "" echo %z
)
That is if I had to do this using batch. But I wouldn't dare to call this "modern" as per your question.
perl is really the best choice for your case, and millions of others. It is very common and the web is ripe with examples and documentation. Yes it is cross platform, extremely stable, and nearly perfectly consistent across platforms. I say nearly because nothing is perfect and I doubt in your lifetime that you would encounter an inconsistency.
It is a language interpreter but supports a rich command-line interface as well.

Grep for beginning and end of line?

I have a file where I want to grep for lines that start with either -rwx or drwx AND end in any number.
I've got this, but it isnt quite right. Any ideas?
grep [^.rwx]*[0-9] usrLog.txt
The tricky part is a regex that includes a dash as one of the valid characters in a character class. The dash has to come immediately after the start for a (normal) character class and immediately after the caret for a negated character class. If you need a close square bracket too, then you need the close square bracket followed by the dash. Mercifully, you only need dash, hence the notation chosen.
grep '^[-d]rwx.*[0-9]$' "$#"
See: Regular Expressions and grep for POSIX-standard details.
It looks like you were on the right track... The ^ character matches beginning-of-line, and $ matches end-of-line. Jonathan's pattern will work for you... just wanted to give you the explanation behind it
It should be noted that not only will the caret (^) behave differently within the brackets, it will have the opposite result of placing it outside of the brackets. Placing the caret where you have it will search for all strings NOT beginning with the content you placed within the brackets. You also would want to place a period before the asterisk in between your brackets as with grep, it also acts as a "wildcard".
grep ^[.rwx].*[0-9]$
This should work for you, I noticed that some posters used a character class in their expressions which is an effective method as well, but you were not using any in your original expression so I am trying to get one as close to yours as possible explaining every minor change along the way so that it is better understood. How can we learn otherwise?
You probably want egrep. Try:
egrep '^[d-]rwx.*[0-9]$' usrLog.txt
are you parsing output of ls -l?
If you are, and you just want to get the file name
find . -iname "*[0-9]"
If you have no choice because usrLog.txt is created by something/someone else and you absolutely must use this file, other options include
awk '/^[-d].*[0-9]$/' file
Ruby(1.9+)
ruby -ne 'print if /^[-d].*[0-9]$/' file
Bash
while read -r line ; do case $line in [-d]*[0-9] ) echo $line; esac; done < file
Many answers provided for this question. Just wanted to add one more which uses bashism-
#! /bin/bash
while read -r || [[ -n "$REPLY" ]]; do
[[ "$REPLY" =~ ^(-rwx|drwx).*[[:digit:]]+$ ]] && echo "Got one -> $REPLY"
done <"$1"
#kurumi answer for bash, which uses case is also correct but it will not read last line of file if there is no newline sequence at the end(Just save the file without pressing 'Enter/Return' at the last line).

Resources