CINXE.COM
What The @#$%&! (Heck) is this #! (Hash-Bang) Thingy In My Bash Script | Linux Journal
<!DOCTYPE html> <html lang="en" dir="ltr" prefix="content: http://purl.org/rss/1.0/modules/content/ dc: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ og: http://ogp.me/ns# rdfs: http://www.w3.org/2000/01/rdf-schema# schema: http://schema.org/ sioc: http://rdfs.org/sioc/ns# sioct: http://rdfs.org/sioc/types# skos: http://www.w3.org/2004/02/skos/core# xsd: http://www.w3.org/2001/XMLSchema# " class="no-js wf-loading"> <head> <meta charset="utf-8" /> <meta name="Generator" content="Drupal 9 (https://www.drupal.org)" /> <meta name="MobileOptimized" content="width" /> <meta name="HandheldFriendly" content="true" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="icon" href="/themes/linuxjournal/favicon.ico" type="image/vnd.microsoft.icon" /> <link rel="canonical" href="https://www.linuxjournal.com/content/what-heck-hash-bang-thingy-my-bash-script" /> <link rel="shortlink" href="https://www.linuxjournal.com/node/1340624" /> <title>What The @#$%&! (Heck) is this #! (Hash-Bang) Thingy In My Bash Script | Linux Journal</title> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/align.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/fieldgroup.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/container-inline.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/clearfix.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/details.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/hidden.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/item-list.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/js.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/nowrap.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/position-container.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/progress.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/reset-appearance.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/resize.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/sticky-header.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/system-status-counter.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/system-status-report-counters.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/system-status-report-general-info.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/tablesort.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/system/css/components/tree-child.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/modules/contrib/poll/css/poll.base.css?smwnh4" /> <link rel="stylesheet" media="all" href="/modules/contrib/poll/css/poll.theme.css?smwnh4" /> <link rel="stylesheet" media="all" href="/core/modules/views/css/views.module.css?smwnh4" /> <link rel="stylesheet" media="all" href="/libraries/shariff/shariff.complete.css?smwnh4" /> <link rel="stylesheet" media="all" href="/modules/contrib/webform/modules/webform_bootstrap/css/webform_bootstrap.css?smwnh4" /> <link rel="stylesheet" media="all" href="/themes/linuxjournal/css/style.css?smwnh4" /> <link rel="stylesheet" media="all" href="/themes/linuxjournal/css/fonts.css?smwnh4" /> <!-- <script defer src="https://use.fontawesome.com/releases/v5.0.9/js/all.js" integrity="sha384-8iPTk2s/jMVj81dnzb/iFR2sdA7u06vHJyyLlAd4snFpCl/SnyUjRrbdJsw1pGIl" crossorigin="anonymous"></script>--> <script> // Non-blocking webfonts. (function iife() { 'use strict'; // JS is able to start executing. document.documentElement.classList.remove('no-js'); // Optimization for Repeat Views // Stop early for repeat views that have already cached fonts. if (sessionStorage.fontsLoadedLateef && sessionStorage.fontsLoadedOpensans /* && sessionStorage.fontsLoadedNEWFONT */) { document.documentElement.classList.remove('wf-loading'); document.documentElement.classList.add('wf-opensans'); document.documentElement.classList.add('wf-lateef'); // document.documentElement.classList.add('wf-NEWFONT'); return; } // Inlined Font Face Observer script + Promise polyfill. /*! Font Face Observer v2.0.13 - © Bram Stein. License: BSD-3-Clause */ (function(){'use strict';var f,g=[];function l(a){g.push(a);1==g.length&&f()}function m(){for(;g.length;)g[0](),g.shift()}f=function(){setTimeout(m)};function n(a){this.a=p;this.b=void 0;this.f=[];var b=this;try{a(function(a){q(b,a)},function(a){r(b,a)})}catch(c){r(b,c)}}var p=2;function t(a){return new n(function(b,c){c(a)})}function u(a){return new n(function(b){b(a)})}function q(a,b){if(a.a==p){if(b==a)throw new TypeError;var c=!1;try{var d=b&&b.then;if(null!=b&&"object"==typeof b&&"function"==typeof d){d.call(b,function(b){c||q(a,b);c=!0},function(b){c||r(a,b);c=!0});return}}catch(e){c||r(a,e);return}a.a=0;a.b=b;v(a)}} function r(a,b){if(a.a==p){if(b==a)throw new TypeError;a.a=1;a.b=b;v(a)}}function v(a){l(function(){if(a.a!=p)for(;a.f.length;){var b=a.f.shift(),c=b[0],d=b[1],e=b[2],b=b[3];try{0==a.a?"function"==typeof c?e(c.call(void 0,a.b)):e(a.b):1==a.a&&("function"==typeof d?e(d.call(void 0,a.b)):b(a.b))}catch(h){b(h)}}})}n.prototype.g=function(a){return this.c(void 0,a)};n.prototype.c=function(a,b){var c=this;return new n(function(d,e){c.f.push([a,b,d,e]);v(c)})}; function w(a){return new n(function(b,c){function d(c){return function(d){h[c]=d;e+=1;e==a.length&&b(h)}}var e=0,h=[];0==a.length&&b(h);for(var k=0;k<a.length;k+=1)u(a[k]).c(d(k),c)})}function x(a){return new n(function(b,c){for(var d=0;d<a.length;d+=1)u(a[d]).c(b,c)})};window.Promise||(window.Promise=n,window.Promise.resolve=u,window.Promise.reject=t,window.Promise.race=x,window.Promise.all=w,window.Promise.prototype.then=n.prototype.c,window.Promise.prototype["catch"]=n.prototype.g);}()); (function(){function l(a,b){document.addEventListener?a.addEventListener("scroll",b,!1):a.attachEvent("scroll",b)}function m(a){document.body?a():document.addEventListener?document.addEventListener("DOMContentLoaded",function c(){document.removeEventListener("DOMContentLoaded",c);a()}):document.attachEvent("onreadystatechange",function k(){if("interactive"==document.readyState||"complete"==document.readyState)document.detachEvent("onreadystatechange",k),a()})};function r(a){this.a=document.createElement("div");this.a.setAttribute("aria-hidden","true");this.a.appendChild(document.createTextNode(a));this.b=document.createElement("span");this.c=document.createElement("span");this.h=document.createElement("span");this.f=document.createElement("span");this.g=-1;this.b.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";this.c.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;"; this.f.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";this.h.style.cssText="display:inline-block;width:200%;height:200%;font-size:16px;max-width:none;";this.b.appendChild(this.h);this.c.appendChild(this.f);this.a.appendChild(this.b);this.a.appendChild(this.c)} function t(a,b){a.a.style.cssText="max-width:none;min-width:20px;min-height:20px;display:inline-block;overflow:hidden;position:absolute;width:auto;margin:0;padding:0;top:-999px;white-space:nowrap;font-synthesis:none;font:"+b+";"}function y(a){var b=a.a.offsetWidth,c=b+100;a.f.style.width=c+"px";a.c.scrollLeft=c;a.b.scrollLeft=a.b.scrollWidth+100;return a.g!==b?(a.g=b,!0):!1}function z(a,b){function c(){var a=k;y(a)&&a.a.parentNode&&b(a.g)}var k=a;l(a.b,c);l(a.c,c);y(a)};function A(a,b){var c=b||{};this.family=a;this.style=c.style||"normal";this.weight=c.weight||"normal";this.stretch=c.stretch||"normal"}var B=null,C=null,E=null,F=null;function G(){if(null===C)if(J()&&/Apple/.test(window.navigator.vendor)){var a=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))(?:\.([0-9]+))/.exec(window.navigator.userAgent);C=!!a&&603>parseInt(a[1],10)}else C=!1;return C}function J(){null===F&&(F=!!document.fonts);return F} function K(){if(null===E){var a=document.createElement("div");try{a.style.font="condensed 100px sans-serif"}catch(b){}E=""!==a.style.font}return E}function L(a,b){return[a.style,a.weight,K()?a.stretch:"","100px",b].join(" ")} A.prototype.load=function(a,b){var c=this,k=a||"BESbswy",q=0,D=b||3E3,H=(new Date).getTime();return new Promise(function(a,b){if(J()&&!G()){var M=new Promise(function(a,b){function e(){(new Date).getTime()-H>=D?b():document.fonts.load(L(c,'"'+c.family+'"'),k).then(function(c){1<=c.length?a():setTimeout(e,25)},function(){b()})}e()}),N=new Promise(function(a,c){q=setTimeout(c,D)});Promise.race([N,M]).then(function(){clearTimeout(q);a(c)},function(){b(c)})}else m(function(){function u(){var b;if(b=-1!= f&&-1!=g||-1!=f&&-1!=h||-1!=g&&-1!=h)(b=f!=g&&f!=h&&g!=h)||(null===B&&(b=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))/.exec(window.navigator.userAgent),B=!!b&&(536>parseInt(b[1],10)||536===parseInt(b[1],10)&&11>=parseInt(b[2],10))),b=B&&(f==v&&g==v&&h==v||f==w&&g==w&&h==w||f==x&&g==x&&h==x)),b=!b;b&&(d.parentNode&&d.parentNode.removeChild(d),clearTimeout(q),a(c))}function I(){if((new Date).getTime()-H>=D)d.parentNode&&d.parentNode.removeChild(d),b(c);else{var a=document.hidden;if(!0===a||void 0===a)f=e.a.offsetWidth, g=n.a.offsetWidth,h=p.a.offsetWidth,u();q=setTimeout(I,50)}}var e=new r(k),n=new r(k),p=new r(k),f=-1,g=-1,h=-1,v=-1,w=-1,x=-1,d=document.createElement("div");d.dir="ltr";t(e,L(c,"sans-serif"));t(n,L(c,"serif"));t(p,L(c,"monospace"));d.appendChild(e.a);d.appendChild(n.a);d.appendChild(p.a);document.body.appendChild(d);v=e.a.offsetWidth;w=n.a.offsetWidth;x=p.a.offsetWidth;I();z(e,function(a){f=a;u()});t(e,L(c,'"'+c.family+'",sans-serif'));z(n,function(a){g=a;u()});t(n,L(c,'"'+c.family+'",serif')); z(p,function(a){h=a;u()});t(p,L(c,'"'+c.family+'",monospace'))})})};"object"===typeof module?module.exports=A:(window.FontFaceObserver=A,window.FontFaceObserver.prototype.load=A.prototype.load);}()); // Load and observe Lateef var Lateef = new FontFaceObserver('Lateef', {weight: 400}); Promise.all([Lateef.load()]).then(function() { document.documentElement.classList.remove('wf-loading'); document.documentElement.classList.add('wf-lateef'); // Optimization for Repeat Views // Set a flag in localstorage so repeat views can skip processing FFO. sessionStorage.fontsLoadedLateef = true; }); // Load and observe Open Sans var os300 = new FontFaceObserver('Open Sans', {weight: 300}); var os400 = new FontFaceObserver('Open Sans', {weight: 400}); Promise.all([os300.load(), os400.load()]).then(function() { document.documentElement.classList.remove('wf-loading'); document.documentElement.classList.add('wf-opensans'); // Optimization for Repeat Views // Set a flag in localstorage so repeat views can skip processing FFO. sessionStorage.fontsLoadedOpensans = true; }); // Load and observe NEWFONT /* var NEWFONT = new FontFaceObserver('NEWFONT', {weight: 400}); Promise.all([NEWFONT.load()]).then(function() { document.documentElement.classList.remove('wf-loading'); document.documentElement.classList.add('wf-NEWFONT'); // Optimization for Repeat Views // Set a flag in localstorage so repeat views can skip processing FFO. sessionStorage.fontsLoadedNEWFONT = true; }); */ })(); </script> <script src="https://www.google.com/recaptcha/api.js" async defer></script> <script src=https://slashdot.org/country.js></script> <script src="/themes/linuxjournal/js/ada.js"></script> <script src="/themes/linuxjournal/js/accessibility.js"></script> <script>window.addEventListener('load', function() { new Accessibility(); });</script> <script src="//a.fsdn.com/con/js/sftheme/cmp2.js"></script> <link rel="stylesheet" href="//a.fsdn.com/con/css/sftheme/sandiego/cmp.css" type="text/css"> <link rel="stylesheet" href="/themes/linuxjournal/css/ccpa.css" type="text/css"> <script>window.bizx.cmp.init({ geo: window });</script> <script> function bm_trace() { (function (w,d,t) { _ml = w._ml || {}; _ml.eid = '771'; var s, cd, tag; s = d.getElementsByTagName(t)[0]; cd = new Date(); tag = d.createElement(t); tag.async = 1; tag.src = 'https://ml314.com/tag.aspx?' + cd.getDate() + cd.getMonth(); s.parentNode.insertBefore(tag, s); })(window,document,'script'); } bizx.cmp.ifConsent('', ['all', 'bombora'],bm_trace); </script> </head> <body class="path-node page-node-type-story has-glyphicons"> <a href="#main-content" class="visually-hidden focusable skip-link"> Skip to main content </a> <div class="dialog-off-canvas-main-canvas" data-off-canvas-main-canvas> <div id="wrap"> <div class="leader-wrapper" id="leader-wrapper"> </div> <div class="brand-wrapper" id="brand-wrapper"> <div class="brand container"> <div class="region region-brand"> <a class="logo navbar-btn" href="/" title="Home" rel="home"> <h1><img src="/themes/linuxjournal/images/ljlogo.png" alt="Linux Journal" /></h1> </a> <section id="block-topbannernearlogo" class="block block-block-content block-block-contenta8e75a3d-967c-416d-8944-4015d1f90a84 clearfix"> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><a href="https://www.windriver.com/products/elxr-pro?utm_source=ba&utm_medium=pa&utm_campaign=ba-dg-amer-awa-vsmf-eLxrProLau_lin_dis_09172024"><img alt="Windriver eLxrPro" data-entity-type="file" data-entity-uuid="8ba04368-7803-4329-a196-dc1ea780ec2a" src="/sites/default/files/inline-images/WindRiver-eLxrPro_0.png" width="728" height="90" loading="lazy" /></a></div> </section> </div> </div> <div class="header-wrapper"> <header class="navbar navbar-default container" id="navbar" role="banner"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <div id="navbar-collapse" class="navbar-collapse collapse"> <div class="region region-navigation-collapsible"> <section id="block-linuxjournal-main-menu" class="block block-system block-system-menu-blockmain clearfix navbar__menu col-xs-12 col-sm-8"> <ul class="menu menu--main nav navbar-nav"> <li class="expanded dropdown first"> <a href="/" class="dropdown-toggle" data-toggle="dropdown" data-drupal-link-system-path="<front>">Topics+ <span class="caret"></span></a> <ul class="dropdown-menu"> <li class="first"> <a href="/tag/cloud">Cloud</a> </li> <li> <a href="/tag/containers" data-drupal-link-system-path="taxonomy/term/978">Containers</a> </li> <li> <a href="/tag/desktop">Desktop</a> </li> <li> <a href="/tag/kernel" data-drupal-link-system-path="taxonomy/term/662">Kernel</a> </li> <li> <a href="/tag/mobile">Mobile</a> </li> <li> <a href="/tag/networking">Networking</a> </li> <li> <a href="/tag/privacy">Privacy</a> </li> <li> <a href="/tag/programming">Programming</a> </li> <li> <a href="/tag/security" data-drupal-link-system-path="taxonomy/term/31">Security</a> </li> <li> <a href="/tag/servers">Servers</a> </li> <li class="last"> <a href="/tag/sysadmin" data-drupal-link-system-path="taxonomy/term/21">SysAdmin</a> </li> </ul> </li> <li> <a href="/news" data-drupal-link-system-path="news">News</a> </li> <li class="last"> <a href="/books" data-drupal-link-system-path="books">eBooks</a> </li> </ul> </section> <section class="search-block-form block block-search block-search-form-block clearfix navbar__search col-xs-12 col-sm-4 col-md-4 col-md-offset-0" data-drupal-selector="search-block-form" id="block-linuxjournal-search" role="search"> <h2 class="block-title sr-only">Search</h2> <form action="/search/node" method="get" id="search-block-form" accept-charset="UTF-8"> <div class="form-item js-form-item form-type-search js-form-type-search form-item-keys js-form-item-keys form-no-label form-group"> <label for="edit-keys" class="control-label sr-only">Search</label> <div class="input-group"><input title="Enter the terms you wish to search for." data-drupal-selector="edit-keys" class="form-search form-control" placeholder="Search" type="search" id="edit-keys" name="keys" value="" size="15" maxlength="128" /><span class="input-group-btn"><button type="submit" value="Search" class="button js-form-submit form-submit btn-primary btn icon-only" name=""><span class="sr-only">Search</span><span class="icon glyphicon glyphicon-search" aria-hidden="true"></span></button></span></div> <div id="edit-keys--description" class="description help-block"> Enter the terms you wish to search for. </div> </div> <div class="form-actions form-group js-form-wrapper form-wrapper" data-drupal-selector="edit-actions" id="edit-actions"></div> </form> </section> </div> </div> <div class="navbar-header"> <div class="region region-navigation"> <section id="block-mobilenavigation" class="block block-system block-system-menu-blockmenu-mobile-navbar clearfix col-xs-12 col-md-8 col-md-offset-2 col-lg-8 col-lg-offset-0 navbar__menu navbar__mobilenavigation"> <ul class="menu menu--menu-mobile-navbar nav"> <li class="first"> <a href="/news" data-drupal-link-system-path="news">News</a> </li> <li> <a href="/popular" data-drupal-link-system-path="popular">Popular</a> </li> <li class="last"> <a href="/recent" data-drupal-link-system-path="recent">Recent</a> </li> </ul> </section> </div> </div> </header> </div> </div> <div role="main" class="main-container container js-quickedit-main-content" id="main"> <div class="row"> <section class="col-sm-12"> <div class="highlighted"> <div class="region region-highlighted"> <div data-drupal-messages-fallback class="hidden"></div> </div> </div> <a id="main-content"></a> <div class="region region-content"> <article data-history-node-id="1340624" class="row bs-2col-stacked node node--type-story node--view-mode-full"> <div class="col-sm-12 bs-region bs-region--top"> <div class="field field--name-field-tags field--type-entity-reference field--label-hidden field--items"> <div class="field--item"><div about="/tag/bash-binfmt-binfmtmisc" typeof="schema:Thing"> <h2><a href="/tag/bash-binfmt-binfmtmisc"> <div property="schema:name" class="field field--name-name field--type-string field--label-hidden field--item">Bash binfmt binfmt_misc</div> </a></h2> <span property="schema:name" content="Bash binfmt binfmt_misc" class="hidden"></span> </div> </div> <div class="field--item"><div about="/tag/bash" typeof="schema:Thing"> <h2><a href="/tag/bash"> <div property="schema:name" class="field field--name-name field--type-string field--label-hidden field--item">Bash</div> </a></h2> <span property="schema:name" content="Bash" class="hidden"></span> </div> </div> <div class="field--item"><div about="/tag/shell-scripting" typeof="schema:Thing"> <h2><a href="/tag/shell-scripting"> <div property="schema:name" class="field field--name-name field--type-string field--label-hidden field--item">Shell Scripting</div> </a></h2> <span property="schema:name" content="Shell Scripting" class="hidden"></span> </div> </div> <div class="field--item"><div about="/tag/programming" typeof="schema:Thing"> <h2><a href="/tag/programming"> <div property="schema:name" class="field field--name-name field--type-string field--label-hidden field--item">Programming</div> </a></h2> <span property="schema:name" content="Programming" class="hidden"></span> </div> </div> </div> <div class="field field--name-node-title field--type-ds field--label-hidden field--item"><h1> What The @#$%&! (Heck) is this #! (Hash-Bang) Thingy In My Bash Script </h1> </div> <div class="field field--name-node-author field--type-ds field--label-hidden field--item">by <a title="View user profile." href="/users/mitch-frazier" lang="" about="/users/mitch-frazier" typeof="schema:Person" property="schema:name" datatype="">Mitch Frazier</a></div> <div class="field field--name-node-post-date field--type-ds field--label-hidden field--item">on May 10, 2019</div> </div> <div class="col-sm-9 bs-region bs-region--left"> <div class="shariff" data-services="["twitter","facebook","linkedin","reddit","mail"]" data-theme="white" data-css="complete" data-orientation="horizontal" data-twitter-via="linuxjournal" data-mail-url="mailto:" data-lang="en"> </div> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p> </p> <p>You've seen it a million times—the hash-bang (#!) line at the top of a script—whether it be Bash, Python, Perl or some other scripting language. And, I'm sure you know what its purpose is: it specifies the script interpreter that's used to execute the script. But, do you know how it actually works? Your initial thought might be that your shell (bash) reads that line and then executes the specified interpreter, but that's not at all how it works. How it actually works is the main focus of this post, but I also want to introduce how you can create your own version of "hash-bang" if you're so inclined.</p> <!--break--><p>When you set the executable bit on a script file and then try to execute the file, the filename is passed directly to the kernel; the shell has nothing to do with interpreting the first line in the script. The first two characters in the file (the hash and the bang) are often referred to (when combined into a single word) as the "magic number" of a script file. With this "magic number", the kernel is able to identify the file as a script, and it (the kernel) then reads the first line of the file and starts the script interpreter that's specified in the first line and passes the script filename to the intepreter.</p> <p>Executable file formats are part of the "binfmt" (binary format) code in the kernel. The "binfmt" handling for scripts is found in the file <a class="reference external" href="https://github.com/torvalds/linux/blob/master/fs/binfmt_script.c">binfmt_script.c</a>, and near the bottom you'll see the following code:</p> <div class="highlight"> <pre> <span class="k">static</span> <span class="k">struct</span> <span class="n">linux_binfmt</span> <span class="n">script_format</span> <span class="o">=</span> <span class="p">{</span> <span class="p">.</span><span class="n">module</span> <span class="o">=</span> <span class="n">THIS_MODULE</span><span class="p">,</span> <span class="p">.</span><span class="n">load_binary</span> <span class="o">=</span> <span class="n">load_script</span><span class="p">,</span> <span class="c1">// <<<<< script loading function</span> <span class="p">};</span> <span class="k">static</span> <span class="kt">int</span> <span class="n">__init</span> <span class="nf">init_script_binfmt</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> <span class="n">register_binfmt</span><span class="p">(</span><span class="o">&</span><span class="n">script_format</span><span class="p">);</span> <span class="c1">// <<<<< register the binfmt</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </pre></div> <p>At some point when the kernel is loading, the function <code class="clang c"><span class="n">__init_script_binfmt</span><span class="p">()</span></code> is called to initialize the "script" binfmt handler. The initialization function registers the binfmt with the kernel and specifices that the function <code class="clang c"><span class="n">load_script</span><span class="p">()</span></code> should be called to attempt to load and execute scripts. The kernel puts all of these registered binfmts into a list called <code class="clang c"><span class="n">formats</span></code>.</p> <p>If you now look in the kernel's <a class="reference external" href="https://github.com/torvalds/linux/blob/master/fs/exec.c">exec.c</a> code, where executables are launched, you'll see code that looks like this:</p> <div class="highlight"> <pre> <span class="kt">int</span> <span class="nf">search_binary_handler</span><span class="p">(</span><span class="k">struct</span> <span class="n">linux_binprm</span> <span class="o">*</span><span class="n">bprm</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// ...</span> <span class="n">list_for_each_entry</span><span class="p">(</span><span class="n">fmt</span><span class="p">,</span> <span class="o">&</span><span class="n">formats</span><span class="p">,</span> <span class="n">lh</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// ...</span> <span class="n">retval</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">-></span><span class="n">load_binary</span><span class="p">(</span><span class="n">bprm</span><span class="p">);</span> <span class="c1">// ...</span> <span class="p">}</span> <span class="c1">// ...</span> <span class="p">}</span> </pre></div> <p>Here the kernel is looping through the list of registered binfmts, calling each binfmt's load function in turn until one of them recognizes the file. In the case of scripts, this calls the function <code class="clang c"><span class="n">load_script</span><span class="p">()</span></code> that was referenced above when the script binfmt was registered. The code <code class="clang c"><span class="n">fmt</span><span class="o">-></span><span class="n">load_binary</span><span class="p">()</span></code> is calling the load function indirectly through a pointer to the function, which is why the names are different.</p> <p>If you now go back to <a class="reference external" href="https://github.com/torvalds/linux/blob/master/fs/binfmt_script.c">binfmt_script.c</a>, and find the <code class="clang c"><span class="n">load_script</span><span class="p">()</span></code> function, you'll see code at the top of it that looks like this:</p> <div class="highlight"> <pre> <span class="k">static</span> <span class="kt">int</span> <span class="nf">load_script</span><span class="p">(</span><span class="k">struct</span> <span class="n">linux_binprm</span> <span class="o">*</span><span class="n">bprm</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// ...</span> <span class="cm">/* Not ours to exec if we don't start with "#!". */</span> <span class="k">if</span> <span class="p">((</span><span class="n">bprm</span><span class="o">-></span><span class="n">buf</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="sc">'#'</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">bprm</span><span class="o">-></span><span class="n">buf</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">!=</span> <span class="sc">'!'</span><span class="p">))</span> <span class="k">return</span> <span class="o">-</span><span class="n">ENOEXEC</span><span class="p">;</span> <span class="c1">// ...</span> <span class="p">}</span> </pre></div> <p>And here you can see where the code checks the first two characters of the buffer <code class="clang c"><span class="n">bprm</span><span class="o">-></span><span class="n">buf</span></code> to see if the file starts with the characters "#" and "!" (hash and bang). If the buffer does not start with those two characters, the function returns an error, and the kernel keeps looking through the list of binfmts for a binfmt that can recognize the file. If the file does start with hash-bang, the function loads the requested interpreter, passes the script file name to it, and <a class="reference external" href="https://en.wikipedia.org/wiki/Bob%27s_your_uncle">Bob's your uncle</a>.</p> <p>If the kernel can't find a binfmt that recognizes the file, it returns an error to the caller. Just for fun, to test this out, I tried making an image file executable:</p> <div class="highlight"> <pre> <span class="gp">$</span> chmod +x image.png <span class="gp">$</span> ./image.png </pre></div> <p>What I expected to see was an error something akin to "not an executable"; instead I got this:</p> <div class="highlight"> <pre> <span class="gp">$</span> ./image.png <span class="go">./image.png: line 1: $'\211PNG\r': command not found</span> <span class="go">./image.png: line 2: $'\032': command not found</span> <span class="go">...</span> </pre></div> <p>Which looks suspiciously like bash is trying to interpret the file. And it turns out that's exactly what is happening. If you check the bash man page you'll find this:</p> <blockquote><p>If this execution fails because the file is not in executable format, and the file is not a directory, it is assumed to be a shell script, a file containing shell commands.</p></blockquote> <p>So bash assumes that if the kernel can't execute it, it must be a shell script. Which means that you don't actually have to include <code class="bash"><span class="s2">"#!/bin/sh</span></code> or <code class="bash"><span class="ch">#!/bin/bash</span></code> at the top of your shell scripts if you only start them from a bash shell.</p> <p>Next, I want to look at how you can create your own executable format (without modifying the Linux kernel). Let's assume that I'm a <a class="reference external" href="https://en.wikipedia.org/wiki/Yoda_conditions">yoda coder</a> and that I want to reverse the order of the hash-bang characters in my scripts, in other words, <cite>bang before hash prefer I</cite> (with apologies to Yoda and George Lucas). I can do this using the miscellaneous binary format "binfmt_misc", which you should be able to see a hint of with the <code class="console"><span class="go">mount</span></code> command:</p> <div class="highlight"> <pre> <span class="gp">$</span> mount <span class="p">|</span> grep ^binfmt_misc <span class="go">binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,relatime)</span> </pre></div> <p>Before creating any binfmts, the directory above contains the following:</p> <div class="highlight"> <pre> <span class="gp">$</span> ls -la /proc/sys/fs/binfmt_misc/ <span class="go">total 0</span> <span class="go">--w------- 1 root root 0 May 4 11:59 register</span> <span class="go">-rw-r--r-- 1 root root 0 Apr 30 06:28 status</span> </pre></div> <p>Before I create my own binfmt, I need an "interpreter" that will get executed when I run one of my yoda scripts. For testing, I'll use the following C program, which just prints out its arguments and then copies the contents of any input files passed to it to stdout (essentially a version of the standard Linux command <code class="console"><span class="go">cat</span></code>):</p> <div class="highlight"> <pre> <span class="cp">#include</span> <span class="cpf"><stdio.h></span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">**</span> <span class="n">argv</span><span class="p">)</span> <span class="p">{</span> <span class="kt">int</span> <span class="n">i</span><span class="p">;</span> <span class="c1">// Print arguments.</span> <span class="k">for</span> <span class="p">(</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">argc</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">)</span> <span class="p">{</span> <span class="n">printf</span><span class="p">(</span><span class="s">"Arg %d: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">argv</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span> <span class="p">}</span> <span class="c1">// Copy files to stdout.</span> <span class="k">for</span> <span class="p">(</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">argc</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">)</span> <span class="p">{</span> <span class="kt">FILE</span><span class="o">*</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="s">"r"</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span> <span class="n">fd</span> <span class="p">)</span> <span class="p">{</span> <span class="kt">char</span> <span class="n">s</span><span class="p">[</span><span class="mi">80</span><span class="p">];</span> <span class="k">while</span> <span class="p">(</span> <span class="n">fgets</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">s</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">fd</span><span class="p">)</span> <span class="p">)</span> <span class="p">{</span> <span class="n">fputs</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">stdout</span><span class="p">);</span> <span class="p">}</span> <span class="n">fclose</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </pre></div> <p>Then I compile the intepreter and place its executable in my bin directory:</p> <div class="highlight"> <pre> $ gcc -o /home/user/bin/yoda yoda.c </pre></div> <p>To create my binfmt, I need to write a configuration line to the file <tt class="docutils literal">/proc/sys/fs/binfmt_misc/register</tt> (the Wikipedia <a class="reference external" href="https://en.wikipedia.org/wiki/Binfmt_misc">binfmt_misc</a> page has good information on this configuration line):</p> <div class="highlight"> <pre> <span class="gp">$</span> su <span class="go">Password: *****</span> <span class="gp">#</span> <span class="nb">echo</span> <span class="s1">':YodaFiles:M::!#::/home/user/bin/yoda:'</span> >/proc/sys/fs/binfmt_misc/register <span class="gp">#</span> <span class="nb">exit</span> </pre></div> <p>The "M" in the line above says this binfmt uses a Magic number, and the "!#" (bang hash) in the line specifies the magic number.</p> <p>Now when I list the directory <tt class="docutils literal">/proc/sys/fs/binfmt_misc/,</tt> I see the following:</p> <div class="highlight"> <pre> <span class="gp">$</span> ls -la /proc/sys/fs/binfmt_misc/ <span class="go">total 0</span> <span class="go">--w------- 1 root root 0 May 4 11:59 register</span> <span class="go">-rw-r--r-- 1 root root 0 Apr 30 06:28 status</span> <span class="go">-rw-r--r-- 1 root root 0 May 4 11:59 YodaFiles</span> </pre></div> <p>When I look at the file for my binfmt, I can see that it references my interpreter and my magic number (2123 == !#):</p> <div class="highlight"> <pre> <span class="gp">$</span> cat /proc/sys/fs/binfmt_misc/YodaFiles <span class="go">enabled</span> <span class="go">interpreter /home/user/bin/yoda</span> <span class="go">flags:</span> <span class="go">offset 0</span> <span class="go">magic 2123</span> </pre></div> <p>And now I can set the executable bit on my "script" and have it run via my interpreter:</p> <div class="highlight"> <pre> <span class="gp">$</span> cat test.yoda <span class="go">!# powerful you have become</span> <span class="gp">$</span> chmod +x test.yoda <span class="gp">$</span> ./test.yoda <span class="go">Arg 0: /home/mitch/bin/yoda</span> <span class="go">Arg 1: ./test.yoda</span> <span class="go">!# powerful you have become</span> </pre></div> <p>I can disable and re-enable my binfmt by writing 0 and 1, respectively, to its proc file:</p> <div class="highlight"> <pre> <span class="gp">#</span> <span class="nb">echo</span> <span class="m">0</span> >/proc/sys/fs/binfmt_misc/YodaFiles <span class="c1"># disable</span> <span class="gp">#</span> <span class="nb">echo</span> <span class="m">1</span> >/proc/sys/fs/binfmt_misc/YodaFiles <span class="c1"># re-enable</span> </pre></div> <p>And I can delete it by writing -1 to its proc file:</p> <div class="highlight"> <pre> <span class="gp">#</span> <span class="nb">echo</span> -1 >/proc/sys/fs/binfmt_misc/YodaFiles <span class="gp">#</span> ls /proc/sys/fs/binfmt_misc/YodaFiles <span class="go">ls: cannot access '/proc/sys/fs/binfmt_misc/YodaFiles': No such file or directory</span> </pre></div> <p>If you want to create a persistent miscellanenous binfmt, you can create a configuration file for it (<tt class="docutils literal"><span class="pre">/etc/binfmt.d/*.conf</span></tt>).</p> <p>Obviously, all this is of questionable use for adding interpreters for different text file formats. It's easier just to stick with the hash-bang convention and have your interpreter ignore the first line. However, if you have a binary file, that may not be an option. Using binfmt_misc, you can associate your file with an interpreter. Note that binfmt_misc also allows you to associate a file with an interpreter based on its file extension, which comes in handy if your files don't always start with the value.</p> <p>P.S. Using punctuation characters to refer to curse words is called a <a class="reference external" href="https://en.wiktionary.org/wiki/grawlix">grawlix</a>, with "@#$%&!" being the <cite>standard</cite>.</p> <hr class="docutils" /><p>Any code found in my articles that is not taken from other sources, should be considered licensed as follows:</p> <div class="highlight"> <pre> <span class="c1"># Copyright 2019 Mitch Frazier <mitch -at- linuxjournal.com></span> <span class="c1">#</span> <span class="c1"># This software may be used and distributed according to the terms of the</span> <span class="c1"># MIT License or the GNU General Public License version 2 (or any later version).</span> </pre></div> </div> <div class="field field--name-dynamic-token-fieldnode-author field--type-ds field--label-hidden field--item"><div> <img loading="lazy" src="/sites/default/files/pictures/picture-801406.jpg" width="75" height="83" typeof="foaf:Image" class="img-responsive" /> </div> <p>Mitch Frazier is an embedded systems programmer at Emerson Electric Co. Mitch has been a contributor to and a friend of <em>Linux Journal </em>since the early 2000s.</p> </div> <section class="section--disqus"> <a id="comments-link" class="btn btn-default" href="https://www.linuxjournal.com/content/what-heck-hash-bang-thingy-my-bash-script#disqus_thread">Load Disqus comments</a> <div id="disqus_thread"></div> <noscript>Our discussions are <a href="https://disqus.com/?ref_noscript" rel="nofollow">powered by Disqus</a>, which require JavaScript.</noscript> </section> </div> <div class="col-sm-3 bs-region bs-region--right"> <div class="field field--name-dynamic-block-fieldnode-sidebar-image field--type-ds field--label-hidden field--item"><div class="views-element-container form-group"><div class="view view-sidebar-image view-id-sidebar_image view-display-id-block_1 js-view-dom-id-b25ecef5ef31acaa96d0806f58c7829edc58a3849b577f8255ced8bccec0f92f"> </div> </div> </div> <div class="field field--name-dynamic-block-fieldnode-recent-content field--type-ds field--label-hidden field--item"><div class="views-element-container form-group"><div class="view view-related-content view-id-related_content view-display-id-block_2 js-view-dom-id-e45dc0b9a638e5b283a12206ed2b5d2668d5105dac57a147dd64771b357d5140"> <div class="view-header"> <h2 class="block-title">Recent Articles</h2> </div> <div class="view-content"> <div class="views-row"><div class="views-field views-field-field-node-image"><div class="field-content"> <a href="/content/using-maxqda-qualitative-data-analysis-linux" hreflang="en"><img loading="lazy" src="/sites/default/files/styles/75x75_square/public/nodeimage/story/using-maxqda-for-qualitative-data-analysis-on-linux.jpg?itok=KFsvAt23" width="75" height="75" alt="Using MAXQDA for Qualitative Data Analysis on Linux" typeof="foaf:Image" class="img-responsive" /> </a> </div></div><div class="views-field views-field-title"><span class="field-content"><a href="/content/using-maxqda-qualitative-data-analysis-linux" hreflang="en">Using MAXQDA for Qualitative Data Analysis on Linux</a></span></div><div class="views-field views-field-uid"><span class="field-content"><a href="/users/george-whittaker" hreflang="en">George Whittaker</a></span></div></div> <div class="views-row"><div class="views-field views-field-field-node-image"><div class="field-content"> <a href="/content/haproxy-ubuntu-load-balancing-and-failover-resilient-infrastructure" hreflang="en"><img loading="lazy" src="/sites/default/files/styles/75x75_square/public/nodeimage/story/haproxy-on-ubuntu-load-balancing-and-failover-for-resilient-infrastructure.jpg?itok=2rVXWl_c" width="75" height="75" alt="HAProxy on Ubuntu: Load Balancing and Failover for Resilient Infrastructure" typeof="foaf:Image" class="img-responsive" /> </a> </div></div><div class="views-field views-field-title"><span class="field-content"><a href="/content/haproxy-ubuntu-load-balancing-and-failover-resilient-infrastructure" hreflang="en">HAProxy on Ubuntu: Load Balancing and Failover for Resilient Infrastructure</a></span></div><div class="views-field views-field-uid"><span class="field-content"><a href="/users/germansuarez" hreflang="en">german.suarez</a></span></div></div> <div class="views-row"><div class="views-field views-field-field-node-image"><div class="field-content"> <a href="/content/linux-binary-analysis-reverse-engineering-and-vulnerability-discovery" hreflang="en"><img loading="lazy" src="/sites/default/files/styles/75x75_square/public/nodeimage/story/linux-binary-analysis-for-reverse-engineering-and-vulnerability-discovery.jpg?itok=vTlNabrQ" width="75" height="75" alt="Linux Binary Analysis for Reverse Engineering and Vulnerability Discovery" typeof="foaf:Image" class="img-responsive" /> </a> </div></div><div class="views-field views-field-title"><span class="field-content"><a href="/content/linux-binary-analysis-reverse-engineering-and-vulnerability-discovery" hreflang="en">Linux Binary Analysis for Reverse Engineering and Vulnerability Discovery</a></span></div><div class="views-field views-field-uid"><span class="field-content"><a href="/users/george-whittaker" hreflang="en">George Whittaker</a></span></div></div> <div class="views-row"><div class="views-field views-field-field-node-image"><div class="field-content"> <a href="/content/debian-backup-and-recovery-solutions-safeguard-your-data-confidence" hreflang="en"><img loading="lazy" src="/sites/default/files/styles/75x75_square/public/nodeimage/story/debian-backup-and-recovery-solutions-safeguard-your-data-with-confidence.jpg?itok=LhESic3E" width="75" height="75" alt="Debian Backup and Recovery Solutions: Safeguard Your Data with Confidence" typeof="foaf:Image" class="img-responsive" /> </a> </div></div><div class="views-field views-field-title"><span class="field-content"><a href="/content/debian-backup-and-recovery-solutions-safeguard-your-data-confidence" hreflang="en">Debian Backup and Recovery Solutions: Safeguard Your Data with Confidence</a></span></div><div class="views-field views-field-uid"><span class="field-content"><a href="/users/george-whittaker" hreflang="en">George Whittaker</a></span></div></div> <div class="views-row"><div class="views-field views-field-field-node-image"><div class="field-content"> <a href="/content/installing-development-tools-debian-setting-compilers-libraries-and-ides-robust-development" hreflang="en"><img loading="lazy" src="/sites/default/files/styles/75x75_square/public/nodeimage/story/installing-development-tools-on-debian-setting-up-compilers-libraries-and-ides-for-a-robust-development-environment.jpg?itok=Aq209Ota" width="75" height="75" alt="Installing Development Tools on Debian: Setting Up Compilers, Libraries, and IDEs for a Robust Development Environment" typeof="foaf:Image" class="img-responsive" /> </a> </div></div><div class="views-field views-field-title"><span class="field-content"><a href="/content/installing-development-tools-debian-setting-compilers-libraries-and-ides-robust-development" hreflang="en">Installing Development Tools on Debian: Setting Up Compilers, Libraries, and IDEs for a Robust Development Environment</a></span></div><div class="views-field views-field-uid"><span class="field-content"><a href="/users/george-whittaker" hreflang="en">George Whittaker</a></span></div></div> <div class="views-row"><div class="views-field views-field-field-node-image"><div class="field-content"> <a href="/content/building-your-own-ubuntu-personal-cloud-step-step-guide-creating-secure-data-haven" hreflang="en"><img loading="lazy" src="/sites/default/files/styles/75x75_square/public/nodeimage/story/building-your-own-ubuntu-personal-cloud-a-step-by-step-guide-to-creating-a-secure-data-haven.jpg?itok=zYkD9lta" width="75" height="75" alt="Building Your Own Ubuntu Personal Cloud: A Step-by-Step Guide to Creating a Secure Data Haven" typeof="foaf:Image" class="img-responsive" /> </a> </div></div><div class="views-field views-field-title"><span class="field-content"><a href="/content/building-your-own-ubuntu-personal-cloud-step-step-guide-creating-secure-data-haven" hreflang="en">Building Your Own Ubuntu Personal Cloud: A Step-by-Step Guide to Creating a Secure Data Haven</a></span></div><div class="views-field views-field-uid"><span class="field-content"><a href="/users/george-whittaker" hreflang="en">George Whittaker</a></span></div></div> </div> </div> </div> </div> <div class="field field--name-dynamic-block-fieldnode-sponsors field--type-ds field--label-hidden field--item"><div class="views-element-container form-group"><div class="view view-latest-sponsor-block view-id-latest_sponsor_block view-display-id-block_1 js-view-dom-id-0bdf4a893e1f74f4a2deba1af19b34ee91da44e978fb9c848632de7bf825e16e"> <div class="view-header"> </div> <div class="view-content"> <div class="views-row"> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"> </div> </div> </div> </div> </div> </div> </div> </article> </div> </section> </div> </div> <footer class="footer container" role="contentinfo"> <div class="region region-footer"> <section class="views-element-container block block-views block-views-blocknewsletter-promo-block-block-1 clearfix" id="block-views-block-newsletter-promo-block-block-1"> <div class="form-group"><div class="view view-newsletter-promo-block view-id-newsletter_promo_block view-display-id-block_1 js-view-dom-id-a71706a949e963e4a38a361b5b2d57bb2f4ea06e40e8cf59b31b3d715dac59c8"> <div class="view-content"> <div class="views-row"> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><div class="newsletter-bottom"> <div class="col-md-9 col-sm-12 signup-form"> <!--<h3>Linux Journal Week in Review</h3> <p>Sign up to get all the good stuff delivered to your inbox every week.</p> <form id="subForm" class="js-cm-form" action="https://www.createsend.com/t/subscribeerror?description=" method="post" data-id="A61C50BEC994754B1D79C5819EC1255CDBC34DDF3E170B13E2BAC2D68C42BF424F853383C84F5BAE38EAB4CFA5C73907CAC074FF8192503AF80F3699F7FE5CE1"> <div class="flexform">--> <!--<label for="fieldEmail">Email</label> <br />--> <!--<input id="fieldEmail" name="cm-gjjtdh-gjjtdh" type="email" class="js-cm-email-input" placeholder="Enter your email. Get the newsletter." required /> <button class="js-cm-submit-button" type="submit">Sign Up</button> </div> <div> <input id="cm-privacy-consent" name="cm-privacy-consent" required type="checkbox" /> <label for="cm-privacy-consent">I give my consent to be emailed</label> <input id="cm-privacy-consent-hidden" name="cm-privacy-consent-hidden" type="hidden" value="true" /> </div> </form> <script type="text/javascript" src="https://js.createsend1.com/javascript/copypastesubscribeformlogic.js"></script> </div>--> <!--<div class="col-md-3 col-sm-12 subs-callout"> <img src="/sites/default/files/styles/large/public/2019-01/LJ294-Jan2019-Cover_0.jpg" width="100px" /> <h3>The Value of Open Source Journalism</h3> <p> Subscribe and support our coverage for technology's biggest thinkers – with up to 52% savings. </p> <strong><a href="https://www.linuxjournal.com/subscribe">Subscribe <i class="fa fa-angle-double-right" aria-hidden="true"></i></a> </strong> </div>--> </div> </div> </div> </div> </div> </div> </section> </div> <div class="footer-blocks col-sm-12"> <div class="col-md-6 col-sm-12 footer-left"> <div class="region region-footer-left"> <section id="block-connectwithusfooter" class="block block-block-content block-block-content5e722bd4-5e08-454b-8507-956089bfa661 clearfix"> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><div class="footer-heading">Connect With Us <span class="social-media sm-a-no-underline"><a href="https://youtube.com/linuxjournalonline" alt="Linux Journal on YouTube" aria-label="YouTube"><i class="fa fa-youtube fa-2x"></i></a><a href="https://www.facebook.com/linuxjournal/" alt="Linux Journal on Facebook" aria-label="Facebook"><i class="fa fa-facebook-f fa-2x"></i><a href="https://twitter.com/linuxjournal" alt="Linux Journal on Twitter" aria-label="Twitter"><i class="fa fa-twitter fa-2x"></i></a></span></div> <p>Linux Journal, representing 25+ years of publication, is the original magazine of the global Open Source community.</p></div> </section> <section id="block-linuxjournal-block-9" class="block block-block-content block-block-content8669793b-e217-4426-a79e-eb3c21ede127 clearfix"> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><div id="copyright">© 2024 Slashdot Media, LLC. All rights reserved.</div> </div> </section> <section id="block-privacyterms" class="block block-block-content block-block-contenta203b8bd-80ef-4982-bf6e-784dd7b44120 clearfix"> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><ul class="menu menu--footer-submenu nav" id="terms-nav"> <li><a href="https://slashdotmedia.com/privacy-statement/" rel="nofollow" target="_blank">PRIVACY POLICY</a></li> <li><a href="https://slashdotmedia.com/terms-of-use/" rel="nofollow" target="_blank">TERMS OF SERVICE</a></li> <li><a href="/sponsors">ADVERTISE</a></li> </ul></div> </section> </div> </div> <div class="col-md-4 col-sm-8 footer-middle"> <div class="region region-footer-middle"> <nav role="navigation" aria-labelledby="block-footermenucolumn2-menu" id="block-footermenucolumn2"> <h2 class="visually-hidden" id="block-footermenucolumn2-menu">Footer Menu Column 2</h2> <ul class="menu menu--footer-menu-column-2 nav"> <li class="first"> <a href="/content/masthead" data-drupal-link-system-path="node/1007727">Masthead</a> </li> <li> <a href="/author" data-drupal-link-system-path="node/1009249">Authors</a> </li> <li class="last"> <a href="/form/contact" data-drupal-link-system-path="webform/contact">Contact Us</a> </li> </ul> </nav> <nav role="navigation" aria-labelledby="block-footermenucolumn3-menu" id="block-footermenucolumn3"> <h2 class="visually-hidden" id="block-footermenucolumn3-menu">Footer Menu Column 3</h2> <ul class="menu menu--footer-menu-column-3 nav"> <li class="first"> <a href="/rss_feeds" data-drupal-link-system-path="node/1000457">RSS Feeds</a> </li> <li class="last"> <a href="/aboutus" data-drupal-link-system-path="node/1000267">About Us</a> </li> </ul> </nav> </div> </div> </div> </footer> </div> </div> <script type="application/json" data-drupal-selector="drupal-settings-json">{"path":{"baseUrl":"\/","scriptPath":null,"pathPrefix":"","currentPath":"node\/1340624","currentPathIsAdmin":false,"isFront":false,"currentLanguage":"en"},"pluralDelimiter":"\u0003","suppressDeprecationErrors":true,"bootstrap":{"forms_has_error_value_toggle":1,"popover_enabled":1,"popover_animation":1,"popover_auto_close":1,"popover_container":"body","popover_content":"","popover_delay":"0","popover_html":0,"popover_placement":"right","popover_selector":"","popover_title":"","popover_trigger":"click"},"linuxjournal":{"disqus":{"origin":"https:\/\/www.linuxjournal.com","prettyUrl":"\/content\/what-heck-hash-bang-thingy-my-bash-script","fullUrl":"https:\/\/www.linuxjournal.com\/content\/what-heck-hash-bang-thingy-my-bash-script","embedUrl":"https:\/\/linuxjournal.disqus.com\/embed.js","shortname":"linuxjournal"}},"statistics":{"data":{"nid":"1340624"},"url":"\/core\/modules\/statistics\/statistics.php"},"ajaxTrustedUrl":{"\/search\/node":true},"user":{"uid":0,"permissionsHash":"a7b3a803411eb9cbd5d7d374ffb326721ee8274ab5a665df8f38311e3aad858c"}}</script> <script src="/core/assets/vendor/jquery/jquery.min.js?v=3.6.3"></script> <script src="/core/assets/vendor/underscore/underscore-min.js?v=1.13.6"></script> <script src="/core/misc/polyfills/element.matches.js?v=9.5.9"></script> <script src="/core/misc/polyfills/object.assign.js?v=9.5.9"></script> <script src="/core/assets/vendor/once/once.min.js?v=1.0.1"></script> <script src="/core/assets/vendor/jquery-once/jquery.once.min.js?v=9.5.9"></script> <script src="/core/misc/drupalSettingsLoader.js?v=9.5.9"></script> <script src="/core/misc/drupal.js?v=9.5.9"></script> <script src="/core/misc/drupal.init.js?v=9.5.9"></script> <script src="/themes/contrib/bootstrap/js/drupal.bootstrap.js?smwnh4"></script> <script src="/themes/contrib/bootstrap/js/attributes.js?smwnh4"></script> <script src="/themes/contrib/bootstrap/js/theme.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/affix.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/alert.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/button.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/carousel.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/collapse.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/dropdown.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/modal.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/tooltip.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/popover.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/scrollspy.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/tab.js?smwnh4"></script> <script src="/themes/linuxjournal/bootstrap/assets/javascripts/bootstrap/transition.js?smwnh4"></script> <script src="/themes/linuxjournal/js/lj-consentmanager.js?smwnh4"></script> <script src="/modules/contrib/webform/js/webform.behaviors.js?v=9.5.9"></script> <script src="/core/misc/jquery.once.bc.js?v=9.5.9"></script> <script src="/core/misc/states.js?v=9.5.9"></script> <script src="/themes/contrib/bootstrap/js/misc/states.js?smwnh4"></script> <script src="/modules/contrib/webform/js/webform.states.js?v=9.5.9"></script> <script src="/modules/contrib/webform/modules/webform_bootstrap/js/webform_bootstrap.states.js?v=9.5.9"></script> <script src="/themes/contrib/bootstrap/js/popover.js?smwnh4"></script> <script src="/core/modules/statistics/statistics.js?v=9.5.9"></script> <script src="/themes/linuxjournal/js/lj-disqus.js?smwnh4"></script> <script src="/libraries/shariff/shariff.complete.js?v=9.5.9"></script> <!-- START EMBED --> <noscript><img src="https://api.b2c.com/api/noscript-448i7exgpyqpr9c144q.gif"></noscript> <!-- END EMBED --> <!-- Matomo --> <script type="text/javascript"> var _paq = _paq || []; function initPiwik() { _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() { var u="https://analytics.linuxjournal.com/"; _paq.push(['setTrackerUrl', u+'piwik.php']); _paq.push(['setSiteId', '50']); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s); })(); } </script> <noscript><p><img src="https://analytics.linuxjournal.com/piwik.php?idsite=50&rec=1" style="border:0;" alt="" /></p></noscript> <!-- End Matomo Code --> <div class="modal-custom overlay-custom" id="ccpa-modal" style="margin-left: 0; display: none; max-width: 100%; width: 100%"> <div id="modal-content" class="modal-content"> <div class="modal-header" id="ccpa-modal-content-destination"></div> <span class="close" id="modal-close">×</span> </div> </div> </body> </html>