概述
参考文章Shell 是用来解决什么问题的?
可以把 shell 理解为 命令解释器,用来解决用户如何与操作系统通信的问题(进行系统调用)。
操作系统对外提供的接口是“系统调用”,也就是一堆编程用的接口。这些接口一般以C函数的形式暴露给使用者。通过这些接口,开发者可以命令操作系统“启动一个进程”,“查找某个目录下的所有文件”,“将某个文件的权限配置为744”等等。
实际上我们平时编程用的是对系统调用的包装,比如libc里的那些库函数。但无论如何,你总是得写代码才能使用它们。
为了不用使用电脑时每次都编写程序,再编译,再运行才能得到结果这么麻烦,就先写好程序,作为文件保存,然后弄个交互的界面,只要使用者敲一组字符串,就可以调用之前写好的程序文件完成工作。比如我们会在命令行里输入“ls -Rl”这种字符串。这个字符串被翻译成“ls”,“-R”,“-l”。“ls”帮我们找到那个之前写好的程序文件,并启动它;“-R”和“-l”被作为参数传给这个程序,告诉程序走“递归所有子目录”+“输出长格式”这部分代码。
这里输入的字符串通常包括了程序文件的名字(当然有例外,比如 Linux 的 alias 命令,比如 通过链接文件访问源文件),所以通常文件名就成了命令。
在 Linux 的 /bin 目录和 /usr/bin 目录中有 Linux 命令对应的文件,在 Windows 的 %windir% 目录即 操作系统安装硬盘分区的 Windows 目录 和 %windir%system32 目录中有 Windows 命令对应的文件, 此外还可以引入外部命令和自定义命令。
这个负责把用户输入的字符串转换到需要执行的程序,并把结果以某个形式画出来的东西,就叫做“Shell”
在Linux中,bash负责按照某种格式把用户的输出的字符串翻译,比如对于普通非空字符翻译为程序和参数,并尝试去PATH里找对应的程序文件;对“空格”翻译成分隔符;对“$XXX”尝试进行环境变量的替换;对“|”翻译为管道;对 “>”翻译为输出重定向;对一个指令末尾的“&”翻译为将程序转到后台执行……
另一方面终端将stdout、stderr输出的东西画成我们可以看的一坨坨字符,包括字符、字体、颜色、换行、甚至响铃。
对于图形化 shell 是通过鼠标的点击来访问文件的,但实际也是通过文件名,因为每个文件都有包含文件名的完整路径。
Shell(壳) | kernel(核) | |
---|---|---|
Linux | 命令行形式的:bash 、sh 、csh 、ksh 图形化的:KDE、GNOME、CDE、 XFCE | kernel |
Windows | 命令行形式的:command,cmd.exe,Windows PowerShell 图形化的:Windows Explorer | DOS(windows 95) Windows NT (Windows New Technology) (Winidows XP开始使用) |
【bash】 + 【终端】大概可以理解为一个以字符为交互方式的“Shell”。
一个更具体的例子:
在Linux终端中输入“ll”命令,Shell解析了ll,用alias翻译成了ls命令,然后执行对应代码,发起系统调用readdir,并把结果输出在终端上”。
ll是"ls -lh"的alias(输入"alias ll"可以看到)。ls的命令实现在coreutlis包里。这里有对应的源代码:http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ls.c
在2900多行有发起对readdir的调用。用"man readdir"可以看到对这个系统调用的解释。
这里应该明确下发起系统调用readdir的不是Shell自己,而是被Shell执行的ls命令
Shell本身说白了就是个无脑 while fork 循环……
static void
print_dir (char const *name, char const *realname, bool command_line_arg)
{
DIR *dirp;
struct dirent *next;
uintmax_t total_blocks = 0;
static bool first = true;
errno = 0;
dirp = opendir (name);
if (!dirp)
{
file_failure (command_line_arg, _("cannot open directory %s"), name);
return;
}
if (LOOP_DETECT)
{
struct stat dir_stat;
int fd = dirfd (dirp);
/* If dirfd failed, endure the overhead of stat'ing by path */
if ((0 <= fd
? fstat_for_ino (fd, &dir_stat)
: stat_for_ino (name, &dir_stat)) < 0)
{
file_failure (command_line_arg,
_("cannot determine device and inode of %s"), name);
closedir (dirp);
return;
}
/* If we've already visited this dev/inode pair, warn that
we've found a loop, and do not process this directory. */
if (visit_dir (dir_stat.st_dev, dir_stat.st_ino))
{
error (0, 0, _("%s: not listing already-listed directory"),
quotef (name));
closedir (dirp);
set_exit_status (true);
return;
}
dev_ino_push (dir_stat.st_dev, dir_stat.st_ino);
}
clear_files ();
if (recursive || print_dir_name)
{
if (!first)
DIRED_PUTCHAR ('n');
first = false;
DIRED_INDENT ();
char *absolute_name = NULL;
if (print_hyperlink)
{
absolute_name = canonicalize_filename_mode (name, CAN_MISSING);
if (! absolute_name)
file_failure (command_line_arg,
_("error canonicalizing %s"), name);
}
quote_name (realname ? realname : name, dirname_quoting_options, -1,
NULL, true, &subdired_obstack, absolute_name);
free (absolute_name);
DIRED_FPUTS_LITERAL (":n", stdout);
}
/* Read the directory entries, and insert the subfiles into the 'cwd_file'
table. */
while (1)
{
/* Set errno to zero so we can distinguish between a readdir failure
and when readdir simply finds that there are no more entries. */
errno = 0;
next = readdir (dirp);//注意 这里使用了readdir 系统调用
if (next)
{
if (! file_ignored (next->d_name))
{
enum filetype type = unknown;
#if HAVE_STRUCT_DIRENT_D_TYPE
switch (next->d_type)
{
case DT_BLK: type = blockdev; break;
case DT_CHR: type = chardev; break;
case DT_DIR: type = directory; break;
case DT_FIFO: type = fifo; break;
case DT_LNK: type = symbolic_link; break;
case DT_REG: type = normal; break;
case DT_SOCK: type = sock; break;
# ifdef DT_WHT
case DT_WHT: type = whiteout; break;
# endif
}
#endif
total_blocks += gobble_file (next->d_name, type,
RELIABLE_D_INO (next),
false, name);
/* In this narrow case, print out each name right away, so
ls uses constant memory while processing the entries of
this directory. Useful when there are many (millions)
of entries in a directory. */
if (format == one_per_line && sort_type == sort_none
&& !print_block_size && !recursive)
{
/* We must call sort_files in spite of
"sort_type == sort_none" for its initialization
of the sorted_file vector. */
sort_files ();
print_current_files ();
clear_files ();
}
}
}
else if (errno != 0)
{
file_failure (command_line_arg, _("reading directory %s"), name);
if (errno != EOVERFLOW)
break;
}
else
break;
/* When processing a very large directory, and since we've inhibited
interrupts, this loop would take so long that ls would be annoyingly
uninterruptible. This ensures that it handles signals promptly. */
process_signals ();
}
if (closedir (dirp) != 0)
{
file_failure (command_line_arg, _("closing directory %s"), name);
/* Don't return; print whatever we got. */
}
/* Sort the directory contents. */
sort_files ();
/* If any member files are subdirectories, perhaps they should have their
contents listed rather than being mentioned here as files. */
if (recursive)
extract_dirs_from_files (name, false);
if (format == long_format || print_block_size)
{
char const *p;
char buf[LONGEST_HUMAN_READABLE + 1];
DIRED_INDENT ();
p = _("total");
DIRED_FPUTS (p, stdout, strlen (p));
DIRED_PUTCHAR (' ');
p = human_readable (total_blocks, buf, human_output_opts,
ST_NBLOCKSIZE, output_block_size);
DIRED_FPUTS (p, stdout, strlen (p));
DIRED_PUTCHAR ('n');
}
if (cwd_n_used)
print_current_files ();
}
最后
以上就是眯眯眼抽屉为你收集整理的计算机中shell是干什么的的全部内容,希望文章能够帮你解决计算机中shell是干什么的所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复