Сьогодні вкотре наштовхнувся на одну з дрібних і неочевидних речей, які треба «просто знати».
Отже, лінуксовий CGI-sh-скрипт на апачі, точніше, одна з його частин. Шукає назву сканера і пише у файл.
OP={scanimage -f "Device=%d"}
echo "$OP" > z.out
Проблема в тому, що у назві пристрою є два слеші підряд. Так показує scanimage, і саму таку назву він хоче бачити, коли його викликають з іменем пристрою у якості параметра. У вихідний файл вперто пишеться один слеш замість двох. Задній розум відчуває, що десь тут воно бачить спецсимвол, але як його змусити писати те, що треба? man echo вказує на чарівну опцію -Е, яка змушує не інтерпретувати слеші як маркери спецсимволів, але собака зарита не тут — ця опція дефолтна, і навіть явне її вказання ситуацію не розрулює.
Запускаємо scanimage в консолі. Два слеша. Перенаправляємо вивід у файл — два слеша. Пишемо ще один скрипт, точніше, копіюємо вищевказану частину великого скрипта у окремий файл, запускаємо. Два слеша. Але оригінльний скрипт дає один. Перебираємо всі види лапок — один слеш.
Виявляється. Собака була зарита не у наведеному коді, а у заголовку. Великий файл мав
#!/bin/sh
а в малий, тестовий, на автоматі ввели
#!/bin/bash
І що вийшло? sh «з'їв» подвоєний слеш, а bash залишив усе як було. Подальше розслідування показало, що в системі sh посилається на dash, і що справа не у записі в файл, а у виводі echo, чи, ймовірніше, у інтерпритації цього виводу оболонкою. Такі справи.
Яка ж мораль цієї байки? Баги часто сидять не там, де ми їх шукаємо, а у тих місцях, про які забуваємо. (В компанії ключів, гаманців і мобілок, ага!). І навіть така типова штука, як вказаний у першому рядку скрипта інтерпритатор, може у реальному житті виявитися не зовсім типовою.