~/ در لینوکس write سیستم کال

کار این سیستم کال در اصل نوشتنه و چیزی که manual اون به ما میگه(man -s2 write $) اینه که ۳ تا آرگومنت از ما می‌گیره که این آرگومنت ها به ترتیب file descriptor (یا همون fd) و پوینتر به بافر و تعداد بایت هایین که می‌خوایم برامون بنویسه.

WRITE(2)                   Linux Programmer's Manual                  WRITE(2)

NAME
       write - write to a file descriptor

SYNOPSIS
       #include <unistd.h>

       ssize_t write(int fd, const void *buf, size_t count);

DESCRIPTION
       write()  writes  up  to  count bytes from the buffer pointed buf to the
       file referred to by the file descriptor fd.

آرگومنت یکم

اگه نمی‌دونید file descriptor چیه، یه عدده که راه اتصال ما به یه “فایل” بحساب میاد و از اونجایی که تو یونیکس “همه چیز فایله”(!) اون سر این عدد می‌تونه هر چیزی باشه! از یه فایل متنی بگیر تا یه سوکت شبکه از نوع TCP یا حتی یه قسمت(قطعه) سخت افزاری از کامپیوتر.

حالا این عدد رو از کجا گیر میاریم؟

زمانی که یه “فایل” رو با استفاده از یه سیستم کال دیگه به اسم open(که بعدا باهاش آشنا می‌شید) باز می‌کنیم.

این عدد از صفر بزرگتره و ۳تا از این عدد ها هم بصورت پیشفرض(by default) برای هر(همه) برنامه(ها) قابل دسترسی هستن.

اون ۳ عدد کدوم اعدادن؟

اولیش صفره که بهش stdin هم می‌گن. بعدیش یکه که بهش stdout هم می‌گن و آخریش هم دو (است!) که بهش stderr هم گفته میشه. (دقیقا همونایی که تو shell scripting ازشون استفاده می‌کنیم.)

عددی که تو این پست ازش استفاده می‌کنیم یکه و هرچیزی که به اون بفرستیم(یا به عبارت دیگه، تو اون فایل بنویسیم) روی صفحه(ترمینال / shell) نشون داده میشه.

آرگومنت دوم

یه پوینتر به اون چیزی که تو “فایل”مون قراره بنویسیمش.

آرگومنت سوم

یه عدده که مشخص می‌کنه چند بایت‌ رو می‌خوایم تو اون “فایل” بنویسیم.

اگر قرار باشه متن “!hello, world” رو روی صفحه(ترمینال / shell) چاپ کنیم، یا باید از یه تابع(()strlen) برای بدست آوردن طول متنمون استفاده کنیم، یا اینکه خودمون تعداد کاراکتر(بایت) هاشو بدونیم و اون عدد رو برای مقدار دهی این آرگومنت استفاده کنیم. (و برای درک بهتر این موضوع هم قطعا از روش دوم استفاده می‌کنیم!)

هر کاراکتر ASCII چند بایت رو اشغال می‌کنه؟ یک

متن ما چند کاراکتر داره؟

و..

همین؟ نه! ولی برای اینکه هیچوقت یادتون نره که کار اون کاراکتر آخریه چیه(و اگه همچین “باگ”ی رو دیدید بدونید که مشکل کجاست) عدد ۱۳ رو برای آرگومنت سوم استفاده می‌کنیم.

اسم فایلمون رو میزاریم write.c و بعد از include کردن کتابخانه unistd.h میتونیم سیستم کال مورد نظرمون رو استفاده کنیم: (حتی بدون include هم ممکنه ولی توصیه نمیشه به هیچ وجه.)

#include <unistd.h>

int main(void){

  write(1, "hello, world!", 13);

  return 0; 
}

برای کامپایل کردن این کد(و اکثر کدهایی که بعدا قراره بنویسیم) از دستور زیر استفاده می‌کنیم:

$ cc -Wall -pedantic -std=c89 write.c

اجرای اون هم مثل بقیه برنامه هاست و نسبت به مسیر جاری(current working director) میتونید از یکی از این دستورها استفاده کنید:

$ /path/to/a.out

یا

$ ./a.out

خوب؟ درست کار کرد؟

بستگی داره منظورمون از درست چی باشه!

سیستم کال write کارشو کاملا درست انجام داد و هرچیزی که ما بهش گفتیم رو اون جوری که ما گفتیم چاپ کرد.(Garbage In, Garbage Out)

مشکل اینجاست که ما بهش نگفتیم که بعد از چاپ کردن متنمون بره به خط بعدی.

برای حل این مشکل نیاز به استفاده از newline داریم و کاراکتری که برای این منظور استفاده می‌کنیم n\ (است!) اما همیشه یادتون باشه که n\ یه کاراکتر حساب میشه و ما(انسان ها) برای اینکه به ماشین(کامپیوتر) بفهمونیم که این کاراکتر با حرف(کاراکتر) n انگلیسی تفاوت داره، نیاز داریم که اون رو اصطلاحا escape کنیم.

پس؟

#include <unistd.h>

int main(void){

  write(1, "hello, world!\n", 13);

  return 0; 
}

کامپایل کردید و اجرا هم کردید ولی هیچ تغییری نکرد که نکرد!

آرگومنت سوم چی میگه؟ یا بهتره بگیم ما چی گفتیم به ماشین؟

با اینکه در اصل ۱۴ تا کاراکتر داریم هنوز هم داریم به write می‌گیم که ۱۳ کاراکتر بنویسه که قطعا شامل newline نمیشه و برای همینه که هنوز هم خروجی این برنامه خیلی زیبا و اونجوری که ما می‌خواستیم نیست. پس:

#include <unistd.h>

int main(void){

  write(1, "hello, world!\n", 14);

  return 0; 
}

موفق باشید.