# 用c++實作shell
[TOC]
## 純C提示字元
[纯C实现Linux下的Shell](https://blog.csdn.net/qq_39384184/article/details/83275264)
## 系統呼叫
參考資料:
[Linux 系統程式設計 - fd 及 open()、close() 系統呼叫](https://blog.jaycetyle.com/2018/12/linux-fd-open-close/)
[geeksforgeeks: Input-output system calls in C | Create, Open, Close, Read, Write](https://www.geeksforgeeks.org/input-output-system-calls-c-create-open-close-read-write/)
### fd(File Descriptor)
**File descriptor**: Integer that uniquely identifies an open file of the process.
**File Descriptor table**: File descriptor table is the collection of integer array indices that are file descriptors in which elements are pointers to file table entries. One unique file descriptors table is provided in operating system for each process.
**File Descriptor table**: File descriptor table is the collection of integer array indices that are file descriptors in which elements are pointers to file table entries. One unique file descriptors table is provided in operating system for each process.

```cpp=
struct fd {
struct file *file;
unsigned int flags;
};
```
檔案必須先開啟後才能進行讀寫操作。
開啟檔案 -> 回傳一個fd(檔案描述器) -> 透過fd作為參數進行讀寫
常見fd: stdin(0), stdout(1), stderr(2)
### open()
檔案開啟後回傳(int)fd。
``` cpp
int open(const char *name, int flags);
int open(const char *name, int flags, mode_t mode);
```
### close()
使用完fd,將fd從檔案表中移出。
### dup, dup2
[dup、dup2实现文件描述符重定向(标准输入、标准输出、标准错误输出)](https://blog.csdn.net/qq_28114615/article/details/94746655?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control)
[重定向编程 dup和dup2函数](https://blog.csdn.net/u010006102/article/details/39667875?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-2.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-2.control)
```cpp
int dup2(int oldfd, int newfd);
```
dup2用来复制参数oldfd所指的文件描述符,并将oldfd拷贝到参数newfd后一起返回。
case1:参数newfd为一个打开的文件描述符 -> newfd所指的文件会先被关闭。
case2:若newfd=oldfd,则返回newfd,而不关闭newfd所指的文件。
dup2所复制的文件描述符与原来的文件描述符共享各种文件状态。共享所有的锁定,读写位置和各项权限或flags等等.
返回值:
成功 -> 返回新的文件描述符。
出错 -> 返回-1.。
注意:由dup2函数返回的新文件描述符一定是当前可用文件描述符中的最小值。
```cpp=
// 以下兩個等價
int oldfd = open("test.txt", O_RDONLY);
// 使用dup: 先關掉1,重新導向1到oldfd的文件
close(1);
dup(oldfd);
close(oldfd); // 把舊的fd關掉,只留下stdout指向打開文件
// 使用dup2: 把上面動作合併的概念
dup2(oldfd, 1);
close(oldfd);
```
newfd和oldfd具有的共同点:
(1)相同的打开文件(管道)。
(2)相同的文件指针,即两个文件共享一个文件指针。
(3)相同的访问模式。读取、写入。
(4)相同的文件状态标识。
### pipe
參考資料
[ptt神作](https://www.ptt.cc/bbs/b97902HW/M.1268953601.A.BA9.html)
1. 讀和寫的端口是固定的,表示資料流的方向也是固定的。
2. 僅限於具有親屬關係的process(parent process,fork出來的程序)間使用。
[pipe system call](https://www.geeksforgeeks.org/pipe-system-call/)

```cpp
int pipe(int pipefd[2]);
```
## 實作
### file redirector
[stackoverflow範例程式碼(with error detection)](https://stackoverflow.com/questions/11515399/implementing-shell-in-c-and-need-help-handling-input-output-redirection)
```shell
cat 1.txt > 2.txt
```
```
in 1.txt
1 2 3 4 5
```
``` cpp=
#include <bits/stdc++.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
int main() {
int fd;
char *argv[] = {"cat", "1.txt", NULL};
fd = open("2.txt", O_WRONLY); // 打開要輸出出去的檔案,並取得他的file descriptor
dup2(fd, 1); // stdout這個fd,連到2.txt
execvp(argv[0], argv); // 執行cat 1.txt,輸出內容會跑到設定好的2.txt
}
```
## 一般shell指令
```shell
ls /Desktop/
```
```cpp=
#include <bits/stdc++.h>
#include <unistd.h>
using namespace std;
char** split_string(string s, char det) {
vector<string> v;
string tmp = "";
int i = 0, n = (int)s.length();
while(s[i] == ' ') i++;
while(s[n - 1] == ' ') n--;
for(; i <= n; i++) {
if(i == n || s[i] == det) {
v.push_back(tmp);
tmp = "";
} else {
tmp += s[i];
}
}
char **p = (char**)malloc((v.size() + 1) * sizeof(char*));
for(int i = 0; i < (int)v.size(); i++) {
p[i] = (char*)malloc(v[i].length() * sizeof(char));
strcpy(p[i], v[i].c_str());
}
p[v.size()] = NULL;
return p;
}
int main() {
string s = "ls /Users/roylin/Desktop/CP/";
char **argv = split_string(s, ' ');
if (fork() != 0) {
waitpid(-1, NULL, 0);
} else {
execvp(argv[0], argv);
}
}
```