{"id":106,"date":"2015-06-08T12:07:23","date_gmt":"2015-06-08T17:07:23","guid":{"rendered":"https:\/\/adamkuj.net\/blog\/?p=106"},"modified":"2021-05-11T08:59:57","modified_gmt":"2021-05-11T13:59:57","slug":"protip-forcing-clean-exit-status-on-linux-bourne-shell-commandsscripts-with","status":"publish","type":"post","link":"https:\/\/adamkuj.net\/blog\/2015\/06\/08\/protip-forcing-clean-exit-status-on-linux-bourne-shell-commandsscripts-with\/","title":{"rendered":"ProTip: Forcing clean exit status on Linux (Bourne Shell) commands\/scripts with &#8220;;:&#8221;"},"content":{"rendered":"<p>Often times in Linux (or BSD, Unix, etc) you want to trigger external commands, but don&#8217;t care if they succeed or fail. For example, in Debian, you can execute scripts and commands from <code>\/etc\/network\/interfaces<\/code> using the <code>[pre-]up<\/code> and <code>[post-]down<\/code> directives. However, if something goes wrong and the command doesn&#8217;t exit cleanly, the interface action (<code>ifup<\/code> or <code>ifdown<\/code>) will fail. This can happen, for example, when adding a static route (<code>ip route add ...<\/code>) or alias address (<code>ip addr add ...<\/code>) that already exists (<code>\"RTNETLINK answers: File exists\"<\/code>). Another common scenario is using <code>'ethtool'<\/code> to set interface parameters such as Tx\/Rx queue sizes. If the <code>ethtool<\/code> command &#8220;fails&#8221; because the value is already set, that will cause the <code>'ifup'<\/code> or <code>'ifdown'<\/code> command to fail as well.<\/p>\n<p>For example<!--more-->, on this particular NIC, ethtool reports that the Maximum RX ring length is 511 descriptors, with a current (default) size of 200 descriptors:<\/p>\n<p><code>adam-desktop ~ # ethtool -g eth0<br \/>\nRing parameters for eth0:<br \/>\nPre-set maximums:<br \/>\n<strong>RX:\t\t511<\/strong><br \/>\nRX Mini:\t0<br \/>\nRX Jumbo:\t0<br \/>\nTX:\t\t511<br \/>\nCurrent hardware settings:<br \/>\n<strong>RX:\t\t200<\/strong><br \/>\nRX Mini:\t0<br \/>\nRX Jumbo:\t0<br \/>\nTX:\t\t511<\/code><\/p>\n<p>To enhance NIC performance, you can use <code>ethtool<\/code> to max-out the RX descriptors, and the command succeeds (exit status 0):<\/p>\n<p><code>adam-desktop ~ # ethtool -G eth0 rx 511<br \/>\nadam-desktop ~ # echo $?<br \/>\n0<br \/>\nadam-desktop ~ # ethtool -g eth0<br \/>\nRing parameters for eth0:<br \/>\nPre-set maximums:<br \/>\n<strong>RX:\t\t511<\/strong><br \/>\nRX Mini:\t0<br \/>\nRX Jumbo:\t0<br \/>\nTX:\t\t511<br \/>\nCurrent hardware settings:<br \/>\n<strong>RX:\t\t511<\/strong><br \/>\nRX Mini:\t0<br \/>\nRX Jumbo:\t0<br \/>\nTX:\t\t511<\/code><\/p>\n<p>That&#8217;s all fine and dandy. But what but what if you repeat the <code>'ethtool -G eth0 rx 511'<\/code> command?<\/p>\n<p><code>adam-desktop ~ # ethtool -G eth0 rx 511<br \/>\nrx unmodified, ignoring<br \/>\nno ring parameters changed, aborting<br \/>\nadam-desktop ~ # echo $?<br \/>\n80<\/code><\/p>\n<p>If you were running this <code>ethtool<\/code> command as part of a <code>pre-up<\/code> command in Debian&#8217;s <code>\/etc\/network\/interfaces<\/code>, it would work fine the first time you ran <code>'ifup eth0'<\/code>. However, if you then ran <code>'ifdown eth0'<\/code> followed by another <code>'ifup eth0'<\/code>, then eth0 would fail to come up the second time around. That behavior fails the <a href=\"http:\/\/en.wikipedia.org\/wiki\/Principle_of_least_astonishment\" title=\"Principle of Least Astonishment\" target=\"_blank\" rel=\"noopener noreferrer\">principle of least astonishment<\/a>, and is the sort of thing that can cause outages and service failures.<\/p>\n<p>However, simply appending <code>\";:\"<\/code> to the command causes it to always exit with status 0 (success). When used in a the context of a <code>pre-up<\/code> interface directive, you can ensure that the interface will always come up cleanly. i.e.<\/p>\n<p><code>adam-desktop ~ # ethtool -G eth0 rx 511;:<br \/>\nrx unmodified, ignoring<br \/>\nno ring parameters changed, aborting<br \/>\nadam-desktop ~ # echo $?<br \/>\n0<\/code><\/p>\n<p>What&#8217;s happening here is that the semi-colon (<code>;<\/code>) is a command separator, and the colon (<code>:<\/code>) effectively means <code>'true'<\/code>. The exit status of the first command <code>(ethtool -G eth0 rx 511)<\/code> is ignored, and only the exit status of the last command <code>(:)<\/code> is reported.<\/p>\n<p>A more verbose example of this principle in action:<\/p>\n<p><code>adam-desktop ~ # false<br \/>\nadam-desktop ~ # echo $?<br \/>\n1<br \/>\nadam-desktop ~ # true<br \/>\nadam-desktop ~ # echo $?<br \/>\n0<br \/>\nadam-desktop ~ # false;true<br \/>\nadam-desktop ~ # echo $?<br \/>\n0<br \/>\nadam-desktop ~ # true;false<br \/>\nadam-desktop ~ # echo $?<br \/>\n1<\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Often times in Linux (or BSD, Unix, etc) you want to trigger external commands, but don&#8217;t care if they succeed or fail. For example, in Debian, you can execute scripts and commands from \/etc\/network\/interfaces using the [pre-]up and [post-]down directives. However, if something goes wrong and the command doesn&#8217;t exit cleanly, the interface action (ifup [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-106","post","type-post","status-publish","format-standard","hentry","category-networking-tools"],"_links":{"self":[{"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/posts\/106","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/comments?post=106"}],"version-history":[{"count":21,"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/posts\/106\/revisions"}],"predecessor-version":[{"id":315,"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/posts\/106\/revisions\/315"}],"wp:attachment":[{"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/media?parent=106"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/categories?post=106"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/adamkuj.net\/blog\/wp-json\/wp\/v2\/tags?post=106"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}